[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] [taler-exchange] branch stable updated (850d84a -> e140b41)
From: |
gnunet |
Subject: |
[GNUnet-SVN] [taler-exchange] branch stable updated (850d84a -> e140b41) |
Date: |
Thu, 06 Jul 2017 15:06:34 +0200 |
This is an automated email from the git hooks/post-receive script.
marcello pushed a change to branch stable
in repository exchange.
from 850d84a assert non-NULL
add 0c52279 bump versions for release
add 780b716 add missing files for distcheck
add cad6476 match GNUnet API rename, work on #5010
add a57cf67 a bit more work on #5010
add 110d0cc start test case for #4956
add 30f1d7b add new test to ignore files
add 0ef7116 do not show -v option twice in -h
add 1eb739c add --test option to taler-exchange-wirewatch
add 16b7c26 modify fakebank API to allow arbitrary subjects, not just
well-fromed WTIDs
add becb713 fix doxygen
add ba17729 tolerate empty transaction list when returning history
add 0580168 automated refunds of expired reserves now work and tested,
fixes #4956
add 5176b25 error code for merchant's /refund
add 19633be error code for merchant's /refund
add d7e7cf5 error code for merchant's /refund
add 274ec61 error code for merchant's /refund
add b874a4c remove dead or unnecessary macros
add eb35468 fix compiler warning due to possibly unknown switch enum
values being handled poorly
add 0e6c4ba work on #5010
add 7f9e627 Styling, no-split and syntax highlighting for texi
add 75b0879 convert another function for #5010
add d66a29e convert another function for #5010
add d2c7ef5 convert another function for #5010
add 2ec1b05 refactoring /deposit towards new transaction style (#5010)
add 2d5b238 rework /reserve/history to address #5010
add 4cb035c rename file to match content better
add dea0f7c fixing #5010 for /reserve/withdraw
add 3d701e8 fix leaks and typos
add 92e6744 address #5010 for /refund
add 51ee201 address #5010 for /payback
add ae7850a split tracking into track_transfer.c and track_transaction.c
add beea8eb refactor /track logic towards new structure
add a0c66e7 fix #5010 for /track/transfer
add 703c54a fix #5010 for /track/transaction
add f8e6214 split httpd_refresh.c into refresh_melt, refresh_link and
refresh_reveal
add 247f82c move reply_transfer_pending to where it belongs
add 39db1ae address #5010 for /refresh/link
add 0530964 fixing #5010 for /refresh/reveal
add 87e1654 incomplete work on fixing #5010 for /refresh/melt
add fbff951 address #5010 for /refresh/melt
add d4884c0 Fix #5010 for keystate
add ff86339 adapt /admin/add/incoming to #5010
add 6acb027 address #5010 for wirewatch
add 9aa2e94 working on #5010 for aggregator
add 0599b3b need extra font to build on my system
add 2d662e3 fix #5010 for taler-exchange-aggregator
add c42d544 convert rest of exchangedb plugin API to fix #5010-issues
add 560051e migrating auditordb to new API to address #5010
add 4c6ef74 update taler-auditor to fix #5010 in auditor as well,
technically done, but untested
add ba95b05 log interesting errors always
add 0ba8825 expand set of EC codes to better cover merchant /refund
(likely still not perfect)
add f048de9 preparations for #4840
add d77c416 implement logic to check protocol version compatibility
(#5035)
add f995079 eliminate dead macros
add 5d6dfde fix #4955 in auditordb, clean up fix in exchangedb
add 932d2b3 signature macro to confirm refunds
add 34887e5 importing a 'PS' struct from merchant
add 1910d66 tolerate total-amount==fee in aggregator
add e140b41 log amount involved
No new revisions were added by this update.
Summary of changes:
ChangeLog | 7 +
README | 13 +-
configure.ac | 4 +-
doc/Makefile.am | 5 +-
doc/brown-paper.css | 63 +
doc/docstyle.css | 76 +
doc/highlight.pack.js | 2 +
doc/paper/taler.tex | 1 +
doc/syntax.texi | 44 +
doc/taler-exchange-wirewatch.1 | 3 +
doc/taler-exchange.texi | 4 +
src/auditor/taler-auditor.c | 954 +--
src/auditordb/plugin_auditordb_postgres.c | 2782 +++----
src/auditordb/test_auditordb.c | 120 +-
src/bank-lib/Makefile.am | 2 +-
src/bank-lib/bank_api_common.c | 5 +-
src/bank-lib/fakebank.c | 137 +-
src/bank-lib/test_bank_interpreter.c | 50 +-
src/benchmark/taler-exchange-benchmark.c | 4 +-
src/exchange-lib/Makefile.am | 2 +-
src/exchange-lib/exchange_api_handle.c | 77 +-
src/exchange-lib/exchange_api_refresh_link.c | 6 +-
src/exchange-lib/test_exchange_api.c | 32 +-
src/exchange/.gitignore | 1 +
src/exchange/Makefile.am | 28 +-
src/exchange/taler-exchange-aggregator.c | 831 ++-
src/exchange/taler-exchange-httpd.c | 12 +-
src/exchange/taler-exchange-httpd_admin.c | 147 +-
src/exchange/taler-exchange-httpd_db.c | 2414 +-----
src/exchange/taler-exchange-httpd_db.h | 264 +-
src/exchange/taler-exchange-httpd_deposit.c | 221 +-
src/exchange/taler-exchange-httpd_keystate.c | 231 +-
src/exchange/taler-exchange-httpd_payback.c | 276 +-
src/exchange/taler-exchange-httpd_refresh.c | 755 --
src/exchange/taler-exchange-httpd_refresh.h | 94 -
src/exchange/taler-exchange-httpd_refresh_link.c | 326 +
...posit.h => taler-exchange-httpd_refresh_link.h} | 25 +-
src/exchange/taler-exchange-httpd_refresh_melt.c | 918 +++
...efund.h => taler-exchange-httpd_refresh_melt.h} | 32 +-
src/exchange/taler-exchange-httpd_refresh_reveal.c | 769 ++
...und.h => taler-exchange-httpd_refresh_reveal.h} | 32 +-
src/exchange/taler-exchange-httpd_refund.c | 357 +-
src/exchange/taler-exchange-httpd_reserve.c | 188 -
src/exchange/taler-exchange-httpd_reserve_status.c | 167 +
...und.h => taler-exchange-httpd_reserve_status.h} | 31 +-
.../taler-exchange-httpd_reserve_withdraw.c | 506 ++
...e.h => taler-exchange-httpd_reserve_withdraw.h} | 30 +-
src/exchange/taler-exchange-httpd_responses.c | 691 +-
src/exchange/taler-exchange-httpd_responses.h | 344 +-
.../taler-exchange-httpd_track_transaction.c | 368 +
....h => taler-exchange-httpd_track_transaction.h} | 26 +-
src/exchange/taler-exchange-httpd_track_transfer.c | 486 ++
...ing.h => taler-exchange-httpd_track_transfer.h} | 27 +-
src/exchange/taler-exchange-httpd_tracking.c | 159 -
src/exchange/taler-exchange-wirewatch.c | 77 +-
...=> test-taler-exchange-wirewatch-postgres.conf} | 4 +-
src/exchange/test_taler_exchange_aggregator.c | 10 +-
src/exchange/test_taler_exchange_wirewatch.c | 848 +++
src/exchangedb/perf_taler_exchangedb_interpreter.c | 109 +-
src/exchangedb/plugin_exchangedb_postgres.c | 7851 ++++++++++----------
src/exchangedb/test_exchangedb.c | 218 +-
src/include/taler_auditordb_plugin.h | 137 +-
src/include/taler_error_codes.h | 62 +-
src/include/taler_exchange_service.h | 63 +-
src/include/taler_exchangedb_plugin.h | 317 +-
src/include/taler_fakebank_lib.h | 27 +-
src/include/taler_signatures.h | 24 +
src/json/Makefile.am | 2 +-
src/util/Makefile.am | 2 +-
src/wire/Makefile.am | 5 +-
70 files changed, 12752 insertions(+), 12153 deletions(-)
create mode 100644 doc/brown-paper.css
create mode 100644 doc/docstyle.css
create mode 100644 doc/highlight.pack.js
create mode 100644 doc/syntax.texi
delete mode 100644 src/exchange/taler-exchange-httpd_refresh.c
delete mode 100644 src/exchange/taler-exchange-httpd_refresh.h
create mode 100644 src/exchange/taler-exchange-httpd_refresh_link.c
copy src/exchange/{taler-exchange-httpd_deposit.h =>
taler-exchange-httpd_refresh_link.h} (63%)
create mode 100644 src/exchange/taler-exchange-httpd_refresh_melt.c
copy src/exchange/{taler-exchange-httpd_refund.h =>
taler-exchange-httpd_refresh_melt.h} (60%)
create mode 100644 src/exchange/taler-exchange-httpd_refresh_reveal.c
copy src/exchange/{taler-exchange-httpd_refund.h =>
taler-exchange-httpd_refresh_reveal.h} (55%)
delete mode 100644 src/exchange/taler-exchange-httpd_reserve.c
create mode 100644 src/exchange/taler-exchange-httpd_reserve_status.c
copy src/exchange/{taler-exchange-httpd_refund.h =>
taler-exchange-httpd_reserve_status.h} (60%)
create mode 100644 src/exchange/taler-exchange-httpd_reserve_withdraw.c
rename src/exchange/{taler-exchange-httpd_reserve.h =>
taler-exchange-httpd_reserve_withdraw.h} (64%)
create mode 100644 src/exchange/taler-exchange-httpd_track_transaction.c
copy src/exchange/{taler-exchange-httpd_tracking.h =>
taler-exchange-httpd_track_transaction.h} (65%)
create mode 100644 src/exchange/taler-exchange-httpd_track_transfer.c
rename src/exchange/{taler-exchange-httpd_tracking.h =>
taler-exchange-httpd_track_transfer.h} (64%)
delete mode 100644 src/exchange/taler-exchange-httpd_tracking.c
copy src/exchange/{test-taler-exchange-aggregator-postgres.conf =>
test-taler-exchange-wirewatch-postgres.conf} (94%)
create mode 100644 src/exchange/test_taler_exchange_wirewatch.c
diff --git a/ChangeLog b/ChangeLog
index ee0cae8..c161add 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sun Jun 11 17:03:56 CEST 2017
+ Finish implementation and testing of automated refunding
+ of expired reserves (#4956). -CG
+
+Tue Jun 6 13:53:34 CEST 2017
+ Releasing taler-exchange 0.3.0. -CG
+
Mon Apr 17 01:29:07 CEST 2017
Add support for HTTP body compression (#4982). -CG
diff --git a/README b/README
index 78665b5..d5d76eb 100644
--- a/README
+++ b/README
@@ -17,11 +17,14 @@ Taler is currently developed by a worldwide group of
independent free
software developers and the DECENTRALISE team at Inria Rennes. Taler
is free software and a GNU package (http://www.gnu.org/).
-This is not even a release yet, but some raw development prototype
-that does not work yet. This package also only includes the Taler
-exchange, not the other components of the system.
+This is an alpha release with a few known bugs, lacking a few
+important features, documentation, testing, performance tuning and an
+external security audit. However, you can run the code and it largely
+works fine. that does not work yet. This package also only includes
+the Taler exchange, not the other components of the system.
Documentation about Taler can be found at http://taler.net/.
+Our bug tracker is at https://gnunet.org/bugs/.
Dependencies:
@@ -30,8 +33,8 @@ Dependencies:
These are the direct dependencies for running a Taler exchange:
- GNUnet >= 0.10.2
-- GNU libmicrohttpd >= 0.9.38
-- Postgres >= 9.3
+- GNU libmicrohttpd >= 0.9.55
+- Postgres >= 9.5
diff --git a/configure.ac b/configure.ac
index 715e94e..65efe78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
#
# This file is part of TALER
-# Copyright (C) 2014, 2015, 2016 GNUnet e.V. and Inria
+# Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V. and Inria
#
# 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
@@ -17,7 +17,7 @@
#
#
AC_PREREQ([2.69])
-AC_INIT([taler-exchange], [0.2.0], address@hidden)
+AC_INIT([taler-exchange], [0.3.0], address@hidden)
AC_CONFIG_SRCDIR([src/util/util.c])
AC_CONFIG_HEADERS([taler_config.h])
# support for non-recursive builds
diff --git a/doc/Makefile.am b/doc/Makefile.am
index f8d04db..f8c8741 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -16,6 +16,8 @@ man_MANS = \
taler-exchange-wirewatch.1 \
taler.conf.5
+AM_MAKEINFOHTMLFLAGS = --no-split --css-ref=docstyle.css
--css-ref=brown-paper.css
+
DISTCLEANFILES = \
taler-exchange.cps \
taler-exchange.dvi
@@ -24,7 +26,8 @@ info_TEXINFOS = \
taler-exchange.texi
extra_TEXINFOS = \
fdl-1.3.texi \
- agpl.texi
+ agpl.texi \
+ syntax.texi
EXTRA_DIST = \
coding-style.txt \
$(man_MANS) \
diff --git a/doc/brown-paper.css b/doc/brown-paper.css
new file mode 100644
index 0000000..65e2e79
--- /dev/null
+++ b/doc/brown-paper.css
@@ -0,0 +1,63 @@
+/*
+
+Brown Paper style from goldblog.com.ua (c) Zaripov Yura <address@hidden>
+
+*/
+
+.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 0.5em;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-literal {
+ color:#005599;
+ font-weight:bold;
+}
+
+.hljs,
+.hljs-subst {
+ color: #363c69;
+}
+
+.hljs-string,
+.hljs-title,
+.hljs-section,
+.hljs-type,
+.hljs-attribute,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-built_in,
+.hljs-addition,
+.hljs-variable,
+.hljs-template-tag,
+.hljs-template-variable,
+.hljs-link,
+.hljs-name {
+ color: #2c009f;
+}
+
+.hljs-comment,
+.hljs-quote,
+.hljs-meta,
+.hljs-deletion {
+ color: #802022;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-literal,
+.hljs-doctag,
+.hljs-title,
+.hljs-section,
+.hljs-type,
+.hljs-name,
+.hljs-strong {
+ font-weight: bold;
+}
+
+.hljs-emphasis {
+ font-style: italic;
+}
diff --git a/doc/docstyle.css b/doc/docstyle.css
new file mode 100644
index 0000000..c91d3f7
--- /dev/null
+++ b/doc/docstyle.css
@@ -0,0 +1,76 @@
+html, body {
+ font-size: 1em;
+ text-align: left;
+ text-decoration: none;
+}
+html { background-color: #e7e7e7; }
+
+body {
+ max-width: 74.92em;
+ margin: 0 auto;
+ padding: .5em 1em 1em 1em;
+ background-color: white;
+ border: .1em solid #c0c0c0;
+}
+
+h1, h2, h3, h4 { color: #333; }
+h5, h6, dt { color: #222; }
+
+
+a h3 {
+ color: #005090;
+}
+
+a[href] { color: #005090; }
+a[href]:visited { color: #100070; }
+a[href]:active, a[href]:hover {
+ color: #100070;
+ text-decoration: none;
+}
+
+.linkrow {
+ margin: 3em 0;
+}
+
+.linkrow {
+ text-align: center;
+}
+
+div.example { padding: .8em 1.2em .4em; }
+pre.example { padding: .8em 1.2em; }
+div.example, pre.example {
+ margin: 1em 0 1em 3% ;
+ -webkit-border-radius: .3em;
+ -moz-border-radius: .3em;
+ border-radius: .3em;
+ border: 1px solid #d4cbb6;
+ background-color: #f2efe4;
+}
+div.example > pre.example {
+ padding: 0 0 .4em;
+ margin: 0;
+ border: none;
+}
+
+
+/* This makes the very long tables of contents in Gnulib and other
+ manuals easier to read. */
+.contents ul, .shortcontents ul { font-weight: bold; }
+.contents ul ul, .shortcontents ul ul { font-weight: normal; }
+.contents ul { list-style: none; }
+
+/* For colored navigation bars (Emacs manual): make the bar extend
+ across the whole width of the page and give it a decent height. */
+.header, .node { margin: 0 -1em; padding: 0 1em; }
+.header p, .node p { line-height: 2em; }
+
+/* For navigation links */
+.node a, .header a { display: inline-block; line-height: 2em; }
+.node a:hover, .header a:hover { background: #f2efe4; }
+
+table.cartouche {
+ border-collapse: collapse;
+ border-color: darkred;
+ border-style: solid;
+ border-width: 3px;
+}
diff --git a/doc/highlight.pack.js b/doc/highlight.pack.js
new file mode 100644
index 0000000..b2f9ff2
--- /dev/null
+++ b/doc/highlight.pack.js
@@ -0,0 +1,2 @@
+/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */
+!function(e){var n="object"==typeof window&&window||"object"==typeof
self&&self;"undefined"!=typeof
exports?e(exports):n&&(n.hljs=e({}),"function"==typeof
define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function
n(e){return
e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function
t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return
t&&0===t.index}function a(e){return k.test(e)}function i(e){var
n,t,r,i,o=e.className+" ";i [...]
\ No newline at end of file
diff --git a/doc/paper/taler.tex b/doc/paper/taler.tex
index bfe8987..f341d46 100644
--- a/doc/paper/taler.tex
+++ b/doc/paper/taler.tex
@@ -58,6 +58,7 @@
\usetikzlibrary{calc}
\usepackage{eurosym}
\usepackage[T1]{fontenc}
+\usepackage{lmodern}
\usepackage{verbatim}
\usepackage[utf8]{inputenc}
diff --git a/doc/syntax.texi b/doc/syntax.texi
new file mode 100644
index 0000000..8aca39d
--- /dev/null
+++ b/doc/syntax.texi
@@ -0,0 +1,44 @@
address@hidden Syntax highlighting for texinfo's HTML output
+
address@hidden
+<script src="highlight.pack.js"></script>
+<script>
+var hls = [];
+var syntaxAuto = true;
+addEventListener("DOMContentLoaded", function() {
+ // Highlight blocks with fixed language
+ for (let x of hls) {
+ let next = x[0].nextElementSibling;
+ console.log("next", next);
+ let blocks = next.querySelectorAll("pre.example");
+ for (let i = 0; i < blocks.length; i++) {
+ blocks[i].classList.add("language-" + x[1]);
+ hljs.highlightBlock(blocks[i]);
+ }
+ }
+ // auto-detect other blocks if not disabled
+ if (syntaxAuto) {
+ let blocks = document.querySelectorAll("pre.example");
+ for (let i = 0; i < blocks.length; i++) {
+ hljs.highlightBlock(blocks[i]);
+ }
+ }
+});
+</script>
address@hidden html
+
address@hidden setsyntax{lang}
address@hidden
+<script>
+hls.push([document.currentScript, "\lang\"]);
+</script>
address@hidden html
address@hidden macro
+
address@hidden setsyntaxnoauto{}
address@hidden
+<script>
+syntaxAuto = false;
+</script>
address@hidden html
address@hidden macro
diff --git a/doc/taler-exchange-wirewatch.1 b/doc/taler-exchange-wirewatch.1
index 72006e6..41a4afa 100644
--- a/doc/taler-exchange-wirewatch.1
+++ b/doc/taler-exchange-wirewatch.1
@@ -19,6 +19,9 @@ Use the specified wire plugin and its configuration to talk
to the bank.
.IP "\-h, \-\-help"
Print short help on options.
.B
+.IP "\-T, \-\-test"
+Run in test mode and exit when idle.
+.B
.IP "\-v, \-\-version"
Print version information.
.B
diff --git a/doc/taler-exchange.texi b/doc/taler-exchange.texi
index c9b1b30..97a0d61 100644
--- a/doc/taler-exchange.texi
+++ b/doc/taler-exchange.texi
@@ -6,6 +6,8 @@
@paragraphindent 0
@exampleindent 4
address@hidden syntax.texi
+
@copying
This manual is for the GNU Taler Exchange
(version @value{VERSION}, @value{UPDATED}),
@@ -306,6 +308,7 @@ If you did not specify a prefix, GNUnet will install to
To download and install the GNU Taler exchange, proceeds as follows:
address@hidden shell
@example
$ git clone git://taler.net/exchange
$ cd exchange
@@ -555,6 +558,7 @@ via an environment variable:
@cite{TALER_EXCHANGEDB_POSTGRES_CONFIG}.
via configuration option @cite{db_conn_str}, under section
@cite{[exchangedb-BACKEND]}. For example, the demo exchange is configured as
follows:
@end itemize
address@hidden ini
@example
[exchange]
...
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 42a167d..f7ea947 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -351,16 +351,16 @@ static struct GNUNET_CONTAINER_MultiHashMap
*denominations;
* @param[out] dki set to detailed information about @a denom_pub, NULL if not
found, must
* NOT be freed by caller
* @param[out] dh set to the hash of @a denom_pub, may be NULL
- * @return #GNUNET_OK on success, #GNUNET_NO for not found, #GNUNET_SYSERR for
DB error
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
get_denomination_info (const struct TALER_DenominationPublicKey *denom_pub,
const struct
TALER_EXCHANGEDB_DenominationKeyInformationP **dki,
struct GNUNET_HashCode *dh)
{
struct GNUNET_HashCode hc;
struct TALER_EXCHANGEDB_DenominationKeyInformationP *dkip;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
if (NULL == dh)
dh = &hc;
@@ -375,19 +375,18 @@ get_denomination_info (const struct
TALER_DenominationPublicKey *denom_pub,
{
/* cache hit */
*dki = dkip;
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
dkip = GNUNET_new (struct TALER_EXCHANGEDB_DenominationKeyInformationP);
- ret = edb->get_denomination_info (edb->cls,
- esession,
- denom_pub,
- dkip);
- if (GNUNET_OK != ret)
+ qs = edb->get_denomination_info (edb->cls,
+ esession,
+ denom_pub,
+ dkip);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_free (dkip);
- GNUNET_break (GNUNET_NO == ret);
*dki = NULL;
- return ret;
+ return qs;
}
{
struct TALER_Amount value;
@@ -405,7 +404,7 @@ get_denomination_info (const struct
TALER_DenominationPublicKey *denom_pub,
dh,
dkip,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -516,28 +515,28 @@ struct ReserveSummary
* initialized (so we can determine the currency).
*
* @param[in,out] rs reserve summary to (fully) initialize
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
load_auditor_reserve_summary (struct ReserveSummary *rs)
{
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
uint64_t rowid;
- ret = adb->get_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub,
- &rowid,
- &rs->a_balance,
- &rs->a_withdraw_fee_balance,
- &rs->a_expiration_date);
- if (GNUNET_SYSERR == ret)
+ qs = adb->get_reserve_info (adb->cls,
+ asession,
+ &rs->reserve_pub,
+ &master_pub,
+ &rowid,
+ &rs->a_balance,
+ &rs->a_withdraw_fee_balance,
+ &rs->a_expiration_date);
+ if (0 > qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- if (GNUNET_NO == ret)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
rs->had_ri = GNUNET_NO;
GNUNET_assert (GNUNET_OK ==
@@ -550,7 +549,7 @@ load_auditor_reserve_summary (struct ReserveSummary *rs)
"Creating fresh reserve `%s' with starting balance %s\n",
TALER_B2S (&rs->reserve_pub),
TALER_amount2s (&rs->a_balance));
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
rs->had_ri = GNUNET_YES;
if ( (GNUNET_YES !=
@@ -565,13 +564,13 @@ load_auditor_reserve_summary (struct ReserveSummary *rs)
"currencies for reserve differ");
/* TODO: find a sane way to continue... */
GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Auditor remembers reserve `%s' has balance %s\n",
TALER_B2S (&rs->reserve_pub),
TALER_amount2s (&rs->a_balance));
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -603,6 +602,11 @@ struct ReserveContext
*/
struct TALER_Amount total_fee_balance;
+ /**
+ * Transaction status code, set to error codes if applicable.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+
};
@@ -633,6 +637,7 @@ handle_reserve_in (void *cls,
struct GNUNET_HashCode key;
struct ReserveSummary *rs;
struct GNUNET_TIME_Absolute expiry;
+ enum GNUNET_DB_QueryStatus qs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_reserve_in_serial_id);
@@ -654,11 +659,11 @@ handle_reserve_in (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (credit->currency,
&rs->total_fee));
- if (GNUNET_OK !=
- load_auditor_reserve_summary (rs))
+ if (0 > (qs = load_auditor_reserve_summary (rs)))
{
GNUNET_break (0);
GNUNET_free (rs);
+ rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
@@ -720,26 +725,28 @@ handle_reserve_out (void *cls,
struct TALER_Amount withdraw_fee;
struct GNUNET_TIME_Absolute valid_start;
struct GNUNET_TIME_Absolute expire_withdraw;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_reserve_out_serial_id);
pp.last_reserve_out_serial_id = rowid + 1;
/* lookup denomination pub data (make sure denom_pub is valid, establish
fees) */
- ret = get_denomination_info (denom_pub,
- &dki,
- &wsrd.h_denomination_pub);
- if (GNUNET_SYSERR == ret)
+ qs = get_denomination_info (denom_pub,
+ &dki,
+ &wsrd.h_denomination_pub);
+ if (0 > qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ rc->qs = qs;
return GNUNET_SYSERR;
}
- if (GNUNET_NO == ret)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
report_row_inconsistency ("withdraw",
rowid,
"denomination key not found (foreign key
constraint violated)");
+ rc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_OK;
}
@@ -790,11 +797,12 @@ handle_reserve_out (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (amount_with_fee->currency,
&rs->total_fee));
- if (GNUNET_OK !=
- load_auditor_reserve_summary (rs))
+ qs = load_auditor_reserve_summary (rs);
+ if (0 > qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_free (rs);
+ rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
@@ -856,7 +864,7 @@ handle_payback_by_reserve (void *cls,
struct TALER_PaybackRequestPS pr;
struct TALER_MasterSignatureP msig;
uint64_t rev_rowid;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
const char *rev;
/* should be monotonically increasing */
@@ -892,17 +900,18 @@ handle_payback_by_reserve (void *cls,
&pr.h_denom_pub);
if (NULL == rev)
{
- ret = edb->get_denomination_revocation (edb->cls,
- esession,
- &pr.h_denom_pub,
- &msig,
- &rev_rowid);
- if (GNUNET_SYSERR == ret)
+ qs = edb->get_denomination_revocation (edb->cls,
+ esession,
+ &pr.h_denom_pub,
+ &msig,
+ &rev_rowid);
+ if (0 > qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ rc->qs = qs;
return GNUNET_SYSERR;
}
- if (GNUNET_NO == ret)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
report_row_inconsistency ("payback",
rowid,
@@ -955,11 +964,12 @@ handle_payback_by_reserve (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (amount->currency,
&rs->total_fee));
- if (GNUNET_OK !=
- load_auditor_reserve_summary (rs))
+ qs = load_auditor_reserve_summary (rs);
+ if (0 > qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_free (rs);
+ rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
@@ -1014,6 +1024,7 @@ handle_reserve_closed (void *cls,
struct ReserveContext *rc = cls;
struct GNUNET_HashCode key;
struct ReserveSummary *rs;
+ enum GNUNET_DB_QueryStatus qs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_reserve_close_serial_id);
@@ -1033,11 +1044,12 @@ handle_reserve_closed (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (amount_with_fee->currency,
&rs->total_in));
- if (GNUNET_OK !=
- load_auditor_reserve_summary (rs))
+ qs = load_auditor_reserve_summary (rs);
+ if (0 > qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_free (rs);
+ rc->qs = qs;
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
@@ -1085,14 +1097,15 @@ verify_reserve_balance (void *cls,
struct ReserveSummary *rs = value;
struct TALER_EXCHANGEDB_Reserve reserve;
struct TALER_Amount balance;
+ enum GNUNET_DB_QueryStatus qs;
int ret;
ret = GNUNET_OK;
reserve.pub = rs->reserve_pub;
- if (GNUNET_OK !=
- edb->reserve_get (edb->cls,
- esession,
- &reserve))
+ qs = edb->reserve_get (edb->cls,
+ esession,
+ &reserve);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
char *diag;
@@ -1103,6 +1116,12 @@ verify_reserve_balance (void *cls,
UINT64_MAX,
diag);
GNUNET_free (diag);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ GNUNET_break (0);
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ rc->qs = qs;
return GNUNET_OK;
}
@@ -1199,19 +1218,15 @@ verify_reserve_balance (void *cls,
"Final balance of reserve `%s' is %s, dropping it\n",
TALER_B2S (&rs->reserve_pub),
TALER_amount2s (&balance));
- ret = adb->del_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- goto cleanup;
- }
- if (GNUNET_NO == ret)
+ qs = adb->del_reserve_info (adb->cls,
+ asession,
+ &rs->reserve_pub,
+ &master_pub);
+ if (0 >= qs)
{
- GNUNET_break (0);
- ret = GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ ret = GNUNET_SYSERR;
+ rc->qs = qs;
goto cleanup;
}
}
@@ -1232,22 +1247,27 @@ verify_reserve_balance (void *cls,
TALER_amount2s (&balance));
if (rs->had_ri)
- ret = adb->update_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub,
- &balance,
- &rs->a_withdraw_fee_balance,
- rs->a_expiration_date);
+ qs = adb->update_reserve_info (adb->cls,
+ asession,
+ &rs->reserve_pub,
+ &master_pub,
+ &balance,
+ &rs->a_withdraw_fee_balance,
+ rs->a_expiration_date);
else
- ret = adb->insert_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub,
- &balance,
- &rs->a_withdraw_fee_balance,
- rs->a_expiration_date);
-
+ qs = adb->insert_reserve_info (adb->cls,
+ asession,
+ &rs->reserve_pub,
+ &master_pub,
+ &balance,
+ &rs->a_withdraw_fee_balance,
+ rs->a_expiration_date);
+ if (0 >= qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ ret = GNUNET_SYSERR;
+ rc->qs = qs;
+ }
cleanup:
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
@@ -1262,27 +1282,29 @@ verify_reserve_balance (void *cls,
* Analyze reserves for being well-formed.
*
* @param cls NULL
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on invariant violation
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
analyze_reserves (void *cls)
{
struct ReserveContext rc;
- int ret;
+ enum GNUNET_DB_QueryStatus qsx;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Analyzing reserves\n");
- ret = adb->get_reserve_summary (adb->cls,
- asession,
- &master_pub,
- &rc.total_balance,
- &rc.total_fee_balance);
- if (GNUNET_SYSERR == ret)
+ rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ qsx = adb->get_reserve_summary (adb->cls,
+ asession,
+ &master_pub,
+ &rc.total_balance,
+ &rc.total_fee_balance);
+ if (qsx < 0)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+ return qsx;
}
- if (GNUNET_NO == ret)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
{
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
@@ -1297,45 +1319,45 @@ analyze_reserves (void *cls)
rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
GNUNET_NO);
- if (GNUNET_SYSERR ==
- edb->select_reserves_in_above_serial_id (edb->cls,
- esession,
- pp.last_reserve_in_serial_id,
- &handle_reserve_in,
- &rc))
+ qs = edb->select_reserves_in_above_serial_id (edb->cls,
+ esession,
+ pp.last_reserve_in_serial_id,
+ &handle_reserve_in,
+ &rc);
+ if (qs < 0)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- if (GNUNET_SYSERR ==
- edb->select_reserves_out_above_serial_id (edb->cls,
- esession,
- pp.last_reserve_out_serial_id,
- &handle_reserve_out,
- &rc))
+ qs = edb->select_reserves_out_above_serial_id (edb->cls,
+ esession,
+ pp.last_reserve_out_serial_id,
+ &handle_reserve_out,
+ &rc);
+ if (qs < 0)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- if (GNUNET_SYSERR ==
- edb->select_payback_above_serial_id (edb->cls,
- esession,
- pp.last_reserve_payback_serial_id,
- &handle_payback_by_reserve,
- &rc))
+ qs = edb->select_payback_above_serial_id (edb->cls,
+ esession,
+ pp.last_reserve_payback_serial_id,
+ &handle_payback_by_reserve,
+ &rc);
+ if (qs < 0)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- if (GNUNET_SYSERR ==
- edb->select_reserve_closed_above_serial_id (edb->cls,
- esession,
-
pp.last_reserve_close_serial_id,
- &handle_reserve_closed,
- &rc))
+ qs = edb->select_reserve_closed_above_serial_id (edb->cls,
+ esession,
+
pp.last_reserve_close_serial_id,
+ &handle_reserve_closed,
+ &rc);
+ if (qs < 0)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
@@ -1346,31 +1368,33 @@ analyze_reserves (void *cls)
GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs)
+ return qs;
- if (GNUNET_NO == ret)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
{
- ret = adb->insert_reserve_summary (adb->cls,
- asession,
- &master_pub,
- &rc.total_balance,
- &rc.total_fee_balance);
+ qs = adb->insert_reserve_summary (adb->cls,
+ asession,
+ &master_pub,
+ &rc.total_balance,
+ &rc.total_fee_balance);
}
else
{
- ret = adb->update_reserve_summary (adb->cls,
- asession,
- &master_pub,
- &rc.total_balance,
- &rc.total_fee_balance);
+ qs = adb->update_reserve_summary (adb->cls,
+ asession,
+ &master_pub,
+ &rc.total_balance,
+ &rc.total_fee_balance);
}
- if (GNUNET_OK != ret)
+ if (0 >= qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
report_reserve_balance (&rc.total_balance,
&rc.total_fee_balance);
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -1476,7 +1500,7 @@ struct AggregationContext
/**
* Final result status.
*/
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
};
@@ -1550,9 +1574,9 @@ struct WireCheckContext
const char *method;
/**
- * Set to #GNUNET_SYSERR if there are inconsistencies.
+ * Database transaction status.
*/
- int ok;
+ enum GNUNET_DB_QueryStatus qs;
};
@@ -1862,14 +1886,17 @@ wire_transfer_information_cb (void *cls,
struct TALER_Amount coin_value_without_fee;
struct TALER_EXCHANGEDB_TransactionList *tl;
const struct TALER_CoinPublicInfo *coin;
+ enum GNUNET_DB_QueryStatus qs;
/* Obtain coin's transaction history */
- tl = edb->get_coin_transactions (edb->cls,
+ qs = edb->get_coin_transactions (edb->cls,
esession,
- coin_pub);
- if (NULL == tl)
+ coin_pub,
+ &tl);
+ if ( (qs < 0) ||
+ (NULL == tl) )
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = qs;
report_row_inconsistency ("aggregation",
rowid,
"no transaction history for coin claimed in
aggregation");
@@ -1894,23 +1921,23 @@ wire_transfer_information_cb (void *cls,
break;
}
GNUNET_assert (NULL != coin); /* hard check that switch worked */
- if (GNUNET_OK !=
- get_denomination_info (&coin->denom_pub,
- &dki,
- NULL))
+ qs = get_denomination_info (&coin->denom_pub,
+ &dki,
+ NULL);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- /* This should be impossible from database constraints */
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
edb->free_coin_transaction_list (edb->cls,
tl);
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = qs;
report_row_inconsistency ("aggregation",
rowid,
"could not find denomination key for coin
claimed in aggregation");
return;
}
- /* Check transaction history to see if it supports aggregate valuation */
+ /* Check transaction history to see if it supports aggregate
+ valuation */
check_transaction_history (coin_pub,
h_contract_terms,
merchant_pub,
@@ -1923,7 +1950,7 @@ wire_transfer_information_cb (void *cls,
coin_value,
coin_fee))
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"inconsistent coin value and fee claimed in
aggregation");
@@ -1933,7 +1960,7 @@ wire_transfer_information_cb (void *cls,
TALER_amount_cmp (&computed_value,
&coin_value_without_fee))
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"coin transaction history and aggregation
disagree about coin's contribution");
@@ -1942,7 +1969,7 @@ wire_transfer_information_cb (void *cls,
TALER_amount_cmp (&computed_fees,
coin_fee))
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"coin transaction history and aggregation
disagree about applicable fees");
@@ -1954,7 +1981,7 @@ wire_transfer_information_cb (void *cls,
if (0 != strcmp (wire_method,
wcc->method))
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"wire method of aggregate do not match wire
transfer");
@@ -1963,7 +1990,7 @@ wire_transfer_information_cb (void *cls,
&wcc->h_wire,
sizeof (struct GNUNET_HashCode)))
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"account details of aggregate do not match
account details of wire transfer");
@@ -1973,7 +2000,7 @@ wire_transfer_information_cb (void *cls,
{
/* This should be impossible from database constraints */
GNUNET_break (0);
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"date given in aggregate does not match wire
transfer date");
@@ -1984,7 +2011,7 @@ wire_transfer_information_cb (void *cls,
coin_value,
coin_fee))
{
- wcc->ok = GNUNET_SYSERR;
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"could not calculate contribution of coin");
@@ -1992,10 +2019,15 @@ wire_transfer_information_cb (void *cls,
}
/* Add coin's contribution to total aggregate value */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&wcc->total_deposits,
- &wcc->total_deposits,
- &contribution));
+ if (GNUNET_OK !=
+ TALER_amount_add (&wcc->total_deposits,
+ &wcc->total_deposits,
+ &contribution))
+ {
+ GNUNET_break (0);
+ wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
+ }
}
@@ -2028,7 +2060,7 @@ get_wire_fee (struct AggregationContext *ac,
/* Lookup fee in exchange database */
wfi = GNUNET_new (struct WireFeeInfo);
- if (GNUNET_OK !=
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
edb->get_wire_fee (edb->cls,
esession,
type,
@@ -2123,6 +2155,7 @@ check_wire_out_cb (void *cls,
const struct TALER_Amount *wire_fee;
struct TALER_Amount final_amount;
struct TALER_Amount exchange_gain;
+ enum GNUNET_DB_QueryStatus qs;
/* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_wire_out_serial_id);
@@ -2145,18 +2178,24 @@ check_wire_out_cb (void *cls,
return GNUNET_OK;
}
wcc.method = json_string_value (method);
- wcc.ok = GNUNET_OK;
+ wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
wcc.date = date;
TALER_amount_get_zero (amount->currency,
&wcc.total_deposits);
TALER_JSON_hash (wire,
&wcc.h_wire);
- edb->lookup_wire_transfer (edb->cls,
- esession,
- wtid,
- &wire_transfer_information_cb,
- &wcc);
- if (GNUNET_OK != wcc.ok)
+ qs = edb->lookup_wire_transfer (edb->cls,
+ esession,
+ wtid,
+ &wire_transfer_information_cb,
+ &wcc);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ ac->qs = qs;
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
{
report_row_inconsistency ("wire_out",
rowid,
@@ -2171,7 +2210,7 @@ check_wire_out_cb (void *cls,
if (NULL == wire_fee)
{
GNUNET_break (0);
- ac->ret = GNUNET_SYSERR;
+ ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_SYSERR ==
@@ -2212,7 +2251,7 @@ check_wire_out_cb (void *cls,
&final_amount))
{
GNUNET_break (0);
- ac->ret = GNUNET_SYSERR;
+ ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
@@ -2223,7 +2262,7 @@ check_wire_out_cb (void *cls,
&exchange_gain))
{
GNUNET_break (0);
- ac->ret = GNUNET_SYSERR;
+ ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
@@ -2249,45 +2288,47 @@ check_wire_out_cb (void *cls,
* Analyze the exchange aggregator's payment processing.
*
* @param cls closure
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
analyze_aggregations (void *cls)
{
struct AggregationContext ac;
struct WirePlugin *wc;
struct WireFeeInfo *wfi;
- int have_balance;
+ enum GNUNET_DB_QueryStatus qsx;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Analyzing aggregations\n");
- ac.ret = GNUNET_OK;
+ ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
ac.wire_head = NULL;
ac.wire_tail = NULL;
ac.fee_head = NULL;
ac.fee_tail = NULL;
- have_balance = adb->get_wire_fee_summary (adb->cls,
- asession,
- &master_pub,
- &ac.total_aggregation_fees);
- if (GNUNET_SYSERR == have_balance)
+ qsx = adb->get_wire_fee_summary (adb->cls,
+ asession,
+ &master_pub,
+ &ac.total_aggregation_fees);
+ if (0 > qsx)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+ return qsx;
}
- if (GNUNET_NO == have_balance)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&ac.total_aggregation_fees));
- if (GNUNET_SYSERR ==
- edb->select_wire_out_above_serial_id (edb->cls,
- esession,
- pp.last_wire_out_serial_id,
- &check_wire_out_cb,
- &ac))
+ ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ qs = edb->select_wire_out_above_serial_id (edb->cls,
+ esession,
+ pp.last_wire_out_serial_id,
+ &check_wire_out_cb,
+ &ac);
+ if (0 > qs)
{
- GNUNET_break (0);
- ac.ret = GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ ac.qs = qs;
}
while (NULL != (wc = ac.wire_head))
{
@@ -2305,28 +2346,28 @@ analyze_aggregations (void *cls)
wfi);
GNUNET_free (wfi);
}
- if (GNUNET_OK != ac.ret)
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
+ return ac.qs;
}
- if (GNUNET_NO == have_balance)
- ac.ret = adb->insert_wire_fee_summary (adb->cls,
- asession,
- &master_pub,
- &ac.total_aggregation_fees);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
+ ac.qs = adb->insert_wire_fee_summary (adb->cls,
+ asession,
+ &master_pub,
+ &ac.total_aggregation_fees);
else
- ac.ret = adb->update_wire_fee_summary (adb->cls,
- asession,
- &master_pub,
- &ac.total_aggregation_fees);
- if (GNUNET_OK != ac.ret)
+ ac.qs = adb->update_wire_fee_summary (adb->cls,
+ asession,
+ &master_pub,
+ &ac.total_aggregation_fees);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
+ return ac.qs;
}
report_aggregation_fee_balance (&ac.total_aggregation_fees);
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -2400,8 +2441,6 @@ struct CoinContext
/**
* Current financial risk of the exchange operator with respect
* to key compromise.
- *
- * TODO: not yet properly used!
*/
struct TALER_Amount risk;
@@ -2411,9 +2450,9 @@ struct CoinContext
unsigned int summaries_off;
/**
- * #GNUNET_OK as long as we are fine to commit the result to the #adb.
+ * Transaction status code.
*/
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
};
@@ -2423,32 +2462,32 @@ struct CoinContext
*
* @param denom_hash hash of the public key of the denomination
* @param[out] ds summary to initialize
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
init_denomination (const struct GNUNET_HashCode *denom_hash,
struct DenominationSummary *ds)
{
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
- ret = adb->get_denomination_balance (adb->cls,
+ qs = adb->get_denomination_balance (adb->cls,
asession,
denom_hash,
&ds->denom_balance,
&ds->denom_risk);
- if (GNUNET_OK == ret)
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
ds->in_db = GNUNET_YES;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Starting balance for denomination `%s' is %s\n",
GNUNET_h2s (denom_hash),
TALER_amount2s (&ds->denom_balance));
- return GNUNET_OK;
- }
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
@@ -2460,7 +2499,7 @@ init_denomination (const struct GNUNET_HashCode
*denom_hash,
"Starting balance for denomination `%s' is %s\n",
GNUNET_h2s (denom_hash),
TALER_amount2s (&ds->denom_balance));
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -2485,9 +2524,8 @@ get_denomination_summary (struct CoinContext *cc,
return ds;
ds = GNUNET_new (struct DenominationSummary);
ds->dki = dki;
- if (GNUNET_OK !=
- init_denomination (dh,
- ds))
+ if (0 > (cc->qs = init_denomination (dh,
+ ds)))
{
GNUNET_break (0);
GNUNET_free (ds);
@@ -2523,7 +2561,7 @@ sync_denomination (void *cls,
struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Absolute expire_deposit;
struct GNUNET_TIME_Absolute expire_deposit_grace;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
now = GNUNET_TIME_absolute_get ();
expire_deposit = GNUNET_TIME_absolute_ntoh (dki->properties.expire_deposit);
@@ -2535,12 +2573,12 @@ sync_denomination (void *cls,
/* Denominationkey has expired, book remaining balance of
outstanding coins as revenue; and reduce cc->risk exposure. */
if (ds->in_db)
- ret = adb->del_denomination_balance (adb->cls,
- asession,
- denom_hash);
+ qs = adb->del_denomination_balance (adb->cls,
+ asession,
+ denom_hash);
else
- ret = GNUNET_OK;
- if ( (GNUNET_OK == ret) &&
+ qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
( (0 != ds->denom_risk.value) ||
(0 != ds->denom_risk.fraction) ) )
{
@@ -2555,10 +2593,10 @@ sync_denomination (void *cls,
/* Holy smokes, our risk assessment was inconsistent!
This is really, really bad. */
GNUNET_break (0);
- cc->ret = GNUNET_SYSERR;
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
}
}
- if ( (GNUNET_OK == ret) &&
+ if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
( (0 != ds->denom_balance.value) ||
(0 != ds->denom_balance.fraction) ) )
{
@@ -2567,17 +2605,17 @@ sync_denomination (void *cls,
"Denomination `%s' expired, booking %s in expiration
profits\n",
GNUNET_h2s (denom_hash),
TALER_amount2s (&ds->denom_balance));
- if (GNUNET_OK !=
- adb->insert_historic_denom_revenue (adb->cls,
- asession,
- &master_pub,
- denom_hash,
- expire_deposit,
- &ds->denom_balance))
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ (qs = adb->insert_historic_denom_revenue (adb->cls,
+ asession,
+ &master_pub,
+ denom_hash,
+ expire_deposit,
+ &ds->denom_balance)))
{
/* Failed to store profits? Bad database */
- GNUNET_break (0);
- cc->ret = GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = qs;
}
}
}
@@ -2588,29 +2626,31 @@ sync_denomination (void *cls,
GNUNET_h2s (denom_hash),
TALER_amount2s (&ds->denom_balance));
if (ds->in_db)
- ret = adb->update_denomination_balance (adb->cls,
- asession,
- denom_hash,
- &ds->denom_balance,
- &ds->denom_risk);
+ qs = adb->update_denomination_balance (adb->cls,
+ asession,
+ denom_hash,
+ &ds->denom_balance,
+ &ds->denom_risk);
else
- ret = adb->insert_denomination_balance (adb->cls,
- asession,
- denom_hash,
- &ds->denom_balance,
- &ds->denom_risk);
+ qs = adb->insert_denomination_balance (adb->cls,
+ asession,
+ denom_hash,
+ &ds->denom_balance,
+ &ds->denom_risk);
}
- if (GNUNET_OK != ret)
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
- cc->ret = GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = qs;
}
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
denom_hash,
ds));
GNUNET_free (ds);
- return cc->ret;
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != cc->qs)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
}
@@ -2649,21 +2689,28 @@ withdraw_cb (void *cls,
struct GNUNET_HashCode dh;
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
struct TALER_Amount value;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_assert (rowid >= pp.last_withdraw_serial_id); /* should be
monotonically increasing */
pp.last_withdraw_serial_id = rowid + 1;
- if (GNUNET_OK !=
- get_denomination_info (denom_pub,
- &dki,
- &dh))
+ qs = get_denomination_info (denom_pub,
+ &dki,
+ &dh);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = qs;
return GNUNET_SYSERR;
}
ds = get_denomination_summary (cc,
dki,
&dh);
+ if (NULL == ds)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
TALER_amount_ntoh (&value,
&dki->properties.value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2676,6 +2723,7 @@ withdraw_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2688,6 +2736,7 @@ withdraw_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -2696,6 +2745,7 @@ withdraw_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -2704,6 +2754,7 @@ withdraw_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
return GNUNET_OK;
@@ -2745,16 +2796,18 @@ refresh_session_cb (void *cls,
struct DenominationSummary *dso;
struct TALER_Amount amount_without_fee;
struct TALER_Amount tmp;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_assert (rowid >= pp.last_melt_serial_id); /* should be monotonically
increasing */
pp.last_melt_serial_id = rowid + 1;
- if (GNUNET_OK !=
- get_denomination_info (denom_pub,
- &dki,
- NULL))
+ qs = get_denomination_info (denom_pub,
+ &dki,
+ NULL);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = qs;
return GNUNET_SYSERR;
}
@@ -2792,15 +2845,15 @@ refresh_session_cb (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (amount_with_fee->currency,
&refresh_cost));
-
- if (GNUNET_OK !=
- edb->get_refresh_order (edb->cls,
- esession,
- session_hash,
- num_newcoins,
- new_dp))
+ qs = edb->get_refresh_order (edb->cls,
+ esession,
+ session_hash,
+ num_newcoins,
+ new_dp);
+ if (0 >= qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
/* Update outstanding amounts for all new coin's denominations, and check
@@ -2809,12 +2862,13 @@ refresh_session_cb (void *cls,
for (unsigned int i=0;i<num_newcoins;i++)
{
/* lookup new coin denomination key */
- if (GNUNET_OK !=
- get_denomination_info (&new_dp[i],
- &new_dki[i],
- NULL))
+ qs = get_denomination_info (&new_dp[i],
+ &new_dki[i],
+ NULL);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = qs;
err = GNUNET_YES;
}
GNUNET_CRYPTO_rsa_public_key_free (new_dp[i].rsa_public_key);
@@ -2844,6 +2898,7 @@ refresh_session_cb (void *cls,
&value)) )
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
}
@@ -2860,6 +2915,7 @@ refresh_session_cb (void *cls,
&melt_fee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
}
@@ -2884,6 +2940,11 @@ refresh_session_cb (void *cls,
dsi = get_denomination_summary (cc,
new_dki[i],
&new_dki[i]->properties.denom_hash);
+ if (NULL == dsi)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
TALER_amount_ntoh (&value,
&new_dki[i]->properties.value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2896,6 +2957,7 @@ refresh_session_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -2904,6 +2966,7 @@ refresh_session_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2916,6 +2979,7 @@ refresh_session_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -2924,6 +2988,7 @@ refresh_session_cb (void *cls,
&value))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
}
@@ -2933,6 +2998,11 @@ refresh_session_cb (void *cls,
dso = get_denomination_summary (cc,
dki,
&dki->properties.denom_hash);
+ if (NULL == dso)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
if (GNUNET_SYSERR ==
TALER_amount_subtract (&tmp,
&dso->denom_balance,
@@ -2950,6 +3020,7 @@ refresh_session_cb (void *cls,
/* This should not be possible, unless the AUDITOR
has a bug in tracking total balance. */
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
@@ -2970,6 +3041,7 @@ refresh_session_cb (void *cls,
&rfee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
}
@@ -3020,16 +3092,18 @@ deposit_cb (void *cls,
struct DenominationSummary *ds;
struct TALER_DepositRequestPS dr;
struct TALER_Amount tmp;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_assert (rowid >= pp.last_deposit_serial_id); /* should be
monotonically increasing */
pp.last_deposit_serial_id = rowid + 1;
- if (GNUNET_OK !=
- get_denomination_info (denom_pub,
- &dki,
- NULL))
+ qs = get_denomination_info (denom_pub,
+ &dki,
+ NULL);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ cc->qs = qs;
return GNUNET_SYSERR;
}
@@ -3042,6 +3116,7 @@ deposit_cb (void *cls,
&dr.h_wire))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
@@ -3072,12 +3147,18 @@ deposit_cb (void *cls,
ds = get_denomination_summary (cc,
dki,
&dki->properties.denom_hash);
+ if (NULL == ds)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
if (GNUNET_SYSERR ==
TALER_amount_subtract (&tmp,
&ds->denom_balance,
amount_with_fee))
{
report_emergency (dki);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
ds->denom_balance = tmp;
@@ -3089,6 +3170,7 @@ deposit_cb (void *cls,
/* This should not be possible, unless the AUDITOR
has a bug in tracking total balance. */
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
@@ -3109,6 +3191,7 @@ deposit_cb (void *cls,
&dfee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
}
@@ -3151,16 +3234,17 @@ refund_cb (void *cls,
struct TALER_RefundRequestPS rr;
struct TALER_Amount amount_without_fee;
struct TALER_Amount refund_fee;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_assert (rowid >= pp.last_refund_serial_id); /* should be
monotonically increasing */
pp.last_refund_serial_id = rowid + 1;
- if (GNUNET_OK !=
- get_denomination_info (denom_pub,
- &dki,
- NULL))
+ qs = get_denomination_info (denom_pub,
+ &dki,
+ NULL);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return GNUNET_SYSERR;
}
@@ -3209,12 +3293,18 @@ refund_cb (void *cls,
ds = get_denomination_summary (cc,
dki,
&dki->properties.denom_hash);
+ if (NULL == ds)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
if (GNUNET_OK !=
TALER_amount_add (&ds->denom_balance,
&ds->denom_balance,
&amount_without_fee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -3223,6 +3313,7 @@ refund_cb (void *cls,
&amount_without_fee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -3231,6 +3322,7 @@ refund_cb (void *cls,
&amount_without_fee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -3239,6 +3331,7 @@ refund_cb (void *cls,
&amount_without_fee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
@@ -3254,6 +3347,7 @@ refund_cb (void *cls,
&refund_fee))
{
GNUNET_break (0);
+ cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
}
@@ -3265,34 +3359,35 @@ refund_cb (void *cls,
* Analyze the exchange's processing of coins.
*
* @param cls closure
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
analyze_coins (void *cls)
{
struct CoinContext cc;
- int dret;
+ enum GNUNET_DB_QueryStatus qs;
+ enum GNUNET_DB_QueryStatus qsx;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Analyzing coins\n");
/* setup 'cc' */
- cc.ret = GNUNET_OK;
+ cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
- GNUNET_NO);
- dret = adb->get_balance_summary (adb->cls,
- asession,
- &master_pub,
- &cc.total_denom_balance,
- &cc.deposit_fee_balance,
- &cc.melt_fee_balance,
- &cc.refund_fee_balance,
- &cc.risk);
- if (GNUNET_SYSERR == dret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_NO == dret)
+ GNUNET_NO);
+ qsx = adb->get_balance_summary (adb->cls,
+ asession,
+ &master_pub,
+ &cc.total_denom_balance,
+ &cc.deposit_fee_balance,
+ &cc.melt_fee_balance,
+ &cc.refund_fee_balance,
+ &cc.risk);
+ if (0 > qsx)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+ return qsx;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
{
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
@@ -3312,92 +3407,93 @@ analyze_coins (void *cls)
}
/* process withdrawals */
- if (GNUNET_SYSERR ==
- edb->select_reserves_out_above_serial_id (edb->cls,
- esession,
- pp.last_withdraw_serial_id,
- &withdraw_cb,
- &cc))
+ if (0 >
+ (qs = edb->select_reserves_out_above_serial_id (edb->cls,
+ esession,
+
pp.last_withdraw_serial_id,
+ &withdraw_cb,
+ &cc)) )
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
/* process refunds */
- if (GNUNET_SYSERR ==
- edb->select_refunds_above_serial_id (edb->cls,
- esession,
- pp.last_refund_serial_id,
- &refund_cb,
- &cc))
+ if (0 >
+ (qs = edb->select_refunds_above_serial_id (edb->cls,
+ esession,
+ pp.last_refund_serial_id,
+ &refund_cb,
+ &cc)))
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
/* process refreshs */
- if (GNUNET_SYSERR ==
- edb->select_refreshs_above_serial_id (edb->cls,
- esession,
- pp.last_melt_serial_id,
- &refresh_session_cb,
- &cc))
+ if (0 >
+ (qs = edb->select_refreshs_above_serial_id (edb->cls,
+ esession,
+ pp.last_melt_serial_id,
+ &refresh_session_cb,
+ &cc)))
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
/* process deposits */
- if (GNUNET_SYSERR ==
- edb->select_deposits_above_serial_id (edb->cls,
- esession,
- pp.last_deposit_serial_id,
- &deposit_cb,
- &cc))
+ if (0 >
+ (qs = edb->select_deposits_above_serial_id (edb->cls,
+ esession,
+ pp.last_deposit_serial_id,
+ &deposit_cb,
+ &cc)))
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
/* sync 'cc' back to disk */
+ cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
&sync_denomination,
&cc);
GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
- if (GNUNET_OK != cc.ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_YES == dret)
- dret = adb->update_balance_summary (adb->cls,
- asession,
- &master_pub,
- &cc.total_denom_balance,
- &cc.deposit_fee_balance,
- &cc.melt_fee_balance,
- &cc.refund_fee_balance,
- &cc.risk);
+ if (0 > cc.qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs);
+ return cc.qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
+ qs = adb->update_balance_summary (adb->cls,
+ asession,
+ &master_pub,
+ &cc.total_denom_balance,
+ &cc.deposit_fee_balance,
+ &cc.melt_fee_balance,
+ &cc.refund_fee_balance,
+ &cc.risk);
else
- dret = adb->insert_balance_summary (adb->cls,
- asession,
- &master_pub,
- &cc.total_denom_balance,
- &cc.deposit_fee_balance,
- &cc.melt_fee_balance,
- &cc.refund_fee_balance,
- &cc.risk);
- if (GNUNET_OK != dret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ qs = adb->insert_balance_summary (adb->cls,
+ asession,
+ &master_pub,
+ &cc.total_denom_balance,
+ &cc.deposit_fee_balance,
+ &cc.melt_fee_balance,
+ &cc.refund_fee_balance,
+ &cc.risk);
+ if (0 >= qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
report_denomination_balance (&cc.total_denom_balance,
&cc.risk,
&cc.deposit_fee_balance,
&cc.melt_fee_balance,
&cc.refund_fee_balance);
- return GNUNET_OK;
+ return qs;
}
@@ -3408,9 +3504,9 @@ analyze_coins (void *cls)
* its own transaction scope and must thus be internally consistent.
*
* @param cls closure
- * @param int #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @return transaction status code
*/
-typedef int
+typedef enum GNUNET_DB_QueryStatus
(*Analysis)(void *cls);
@@ -3420,26 +3516,25 @@ typedef int
*
* @param analysis analysis to run
* @param analysis_cls closure for @a analysis
- * @return #GNUNET_OK if @a analysis succeessfully committed,
- * #GNUNET_SYSERR on hard errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
incremental_processing (Analysis analysis,
void *analysis_cls)
{
- int ret;
- int have_pp;
+ enum GNUNET_DB_QueryStatus qs;
+ enum GNUNET_DB_QueryStatus qsx;
- have_pp = adb->get_auditor_progress (adb->cls,
- asession,
- &master_pub,
- &pp);
- if (GNUNET_SYSERR == have_pp)
+ qsx = adb->get_auditor_progress (adb->cls,
+ asession,
+ &master_pub,
+ &pp);
+ if (0 > qsx)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+ return qsx;
}
- if (GNUNET_NO == have_pp)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
_("First analysis using this auditor, starting audit from
scratch\n"));
@@ -3456,27 +3551,33 @@ incremental_processing (Analysis analysis,
(unsigned long long) pp.last_refund_serial_id,
(unsigned long long) pp.last_wire_out_serial_id);
}
- ret = analysis (analysis_cls);
- if (GNUNET_OK != ret)
+ qs = analysis (analysis_cls);
+ if (0 > qs)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Analysis phase failed, not recording progress\n");
- return GNUNET_SYSERR;
- }
- if (GNUNET_YES == have_pp)
- ret = adb->update_auditor_progress (adb->cls,
- asession,
- &master_pub,
- &pp);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Serialization issue, not recording progress\n");
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Hard database error, not recording progress\n");
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
+ qs = adb->update_auditor_progress (adb->cls,
+ asession,
+ &master_pub,
+ &pp);
else
- ret = adb->insert_auditor_progress (adb->cls,
- asession,
- &master_pub,
- &pp);
- if (GNUNET_OK != ret)
+ qs = adb->insert_auditor_progress (adb->cls,
+ asession,
+ &master_pub,
+ &pp);
+ if (0 >= qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Failed to update auditor DB, not recording progress\n");
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Concluded audit step at
%llu/%llu/%llu/%llu/%llu/%llu/%llu\n\n"),
@@ -3487,7 +3588,7 @@ incremental_processing (Analysis analysis,
(unsigned long long) pp.last_melt_serial_id,
(unsigned long long) pp.last_refund_serial_id,
(unsigned long long) pp.last_wire_out_serial_id);
- return GNUNET_OK;
+ return qs;
}
@@ -3506,6 +3607,7 @@ transact (Analysis analysis,
void *analysis_cls)
{
int ret;
+ enum GNUNET_DB_QueryStatus qs;
ret = adb->start (adb->cls,
asession);
@@ -3521,26 +3623,28 @@ transact (Analysis analysis,
GNUNET_break (0);
return GNUNET_SYSERR;
}
- ret = incremental_processing (analysis,
+ qs = incremental_processing (analysis,
analysis_cls);
- if (GNUNET_OK == ret)
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
- ret = edb->commit (edb->cls,
+ qs = edb->commit (edb->cls,
esession);
- if (GNUNET_OK != ret)
+ if (0 > qs)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Exchange DB commit failed, rolling back transaction\n");
adb->rollback (adb->cls,
asession);
}
else
{
- ret = adb->commit (adb->cls,
- asession);
- if (GNUNET_OK != ret)
+ qs = adb->commit (adb->cls,
+ asession);
+ if (0 > qs)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Auditor DB commit failed!\n");
}
}
@@ -3555,7 +3659,7 @@ transact (Analysis analysis,
esession);
}
clear_transaction_state_cache ();
- return ret;
+ return qs;
}
diff --git a/src/auditordb/plugin_auditordb_postgres.c
b/src/auditordb/plugin_auditordb_postgres.c
index f144746..4862cf2 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -31,72 +31,6 @@
/**
- * Log a query error.
- *
- * @param result PQ result object of the query that failed
- */
-#define QUERY_ERR(result) \
- LOG (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s (%s)\n", __FILE__,
__LINE__, PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)))
-
-
-/**
- * Log a really unexpected PQ error.
- *
- * @param result PQ result object of the PQ operation that failed
- */
-#define BREAK_DB_ERR(result) do { \
- GNUNET_break (0); \
- LOG (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s (%s)\n",
PQresultErrorMessage (result), PQresStatus (PQresultStatus (result))); \
- } while (0)
-
-
-/**
- * Shorthand for exit jumps. Logs the current line number
- * and jumps to the "EXITIF_exit" label.
- *
- * @param cond condition that must be TRUE to exit with an error
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Execute an SQL statement and log errors on failure. Must be
- * run in a function that has an "SQLEXEC_fail" label to jump
- * to in case the SQL statement failed.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_(conn, sql) \
- do { \
- PGresult *result = PQexec (conn, sql); \
- if (PGRES_COMMAND_OK != PQresultStatus (result)) \
- { \
- BREAK_DB_ERR (result); \
- PQclear (result); \
- goto SQLEXEC_fail; \
- } \
- PQclear (result); \
- } while (0)
-
-
-/**
- * Run an SQL statement, ignoring errors and clearing the result.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_IGNORE_ERROR_(conn, sql) \
- do { \
- PGresult *result = PQexec (conn, sql); \
- PQclear (result); \
- } while (0)
-
-
-/**
* Handle for a database session (per-thread, for transactions).
*/
struct TALER_AUDITORDB_Session
@@ -203,42 +137,35 @@ static int
postgres_drop_tables (void *cls)
{
struct PostgresClosure *pc = cls;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS predicted_result;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS historic_ledger;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS historic_losses;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS
historic_denomination_revenue;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS balance_summary;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS denomination_pending;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_reserve_balance;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_wire_fee_balance;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_reserves;"),
+ GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_progress;"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
PGconn *conn;
+ int ret;
conn = connect_to_postgres (pc);
if (NULL == conn)
return GNUNET_SYSERR;
LOG (GNUNET_ERROR_TYPE_INFO,
"Dropping ALL tables\n");
+ ret = GNUNET_PQ_exec_statements (conn,
+ es);
/* TODO: we probably need a bit more fine-grained control
over drops for the '-r' option of taler-auditor; also,
for the testcase, we currently fail to drop the
auditor_denominations table... */
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS predicted_result;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS historic_ledger;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS historic_losses;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS historic_denomination_revenue;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS balance_summary;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS denomination_pending;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS auditor_reserve_balance;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS auditor_wire_fee_balance;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS auditor_reserves;");
- SQLEXEC_ (conn,
- "DROP TABLE IF EXISTS auditor_progress;");
- PQfinish (conn);
- return GNUNET_OK;
- SQLEXEC_fail:
PQfinish (conn);
- return GNUNET_SYSERR;
+ return ret;
}
@@ -252,245 +179,222 @@ static int
postgres_create_tables (void *cls)
{
struct PostgresClosure *pc = cls;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ /* Table with all of the denomination keys that the auditor
+ is aware of. */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS auditor_denominations"
+ "(denom_pub_hash BYTEA PRIMARY KEY CHECK
(LENGTH(denom_pub_hash)=64)"
+ ",master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",valid_from INT8 NOT NULL"
+ ",expire_withdraw INT8 NOT NULL"
+ ",expire_deposit INT8 NOT NULL"
+ ",expire_legal INT8 NOT NULL"
+ ",coin_val INT8 NOT NULL" /* value of this denom */
+ ",coin_frac INT4 NOT NULL" /* fractional value of
this denom */
+ ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL" /* assuming same currency for fees */
+ ",fee_withdraw_val INT8 NOT NULL"
+ ",fee_withdraw_frac INT4 NOT NULL"
+ ",fee_withdraw_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_deposit_val INT8 NOT NULL"
+ ",fee_deposit_frac INT4 NOT NULL"
+ ",fee_deposit_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refresh_val INT8 NOT NULL"
+ ",fee_refresh_frac INT4 NOT NULL"
+ ",fee_refresh_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refund_val INT8 NOT NULL"
+ ",fee_refund_frac INT4 NOT NULL"
+ ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR")
NOT NULL"
+ ")"),
+ /* Table indicating up to which transactions the auditor has
+ processed the exchange database. Used for SELECTing the
+ statements to process. We basically trace the exchange's
+ operations by the 6 primary tables: reserves_in,
+ reserves_out, deposits, refresh_sessions, refunds and prewire. The
+ other tables of the exchange DB just provide supporting
+ evidence which is checked alongside the audit of these
+ five tables. The 6 indices below include the last serial
+ ID from the respective tables that we have processed. Thus,
+ we need to select those table entries that are strictly
+ larger (and process in monotonically increasing order). */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS auditor_progress"
+ "(master_pub BYTEA PRIMARY KEY CHECK
(LENGTH(master_pub)=32)"
+ ",last_reserve_in_serial_id INT8 NOT NULL"
+ ",last_reserve_out_serial_id INT8 NOT NULL"
+ ",last_reserve_payback_serial_id INT8 NOT NULL"
+ ",last_reserve_close_serial_id INT8 NOT NULL"
+ ",last_withdraw_serial_id INT8 NOT NULL"
+ ",last_deposit_serial_id INT8 NOT NULL"
+ ",last_melt_serial_id INT8 NOT NULL"
+ ",last_refund_serial_id INT8 NOT NULL"
+ ",last_wire_out_serial_id INT8 NOT NULL"
+ ")"),
+ /* Table with all of the customer reserves and their respective
+ balances that the auditor is aware of.
+ "last_reserve_out_serial_id" marks the last withdrawal from
+ "reserves_out" about this reserve that the auditor is aware of,
+ and "last_reserve_in_serial_id" is the last "reserve_in"
+ operation about this reserve that the auditor is aware of. */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS auditor_reserves"
+ "(reserve_pub BYTEA NOT NULL
CHECK(LENGTH(reserve_pub)=32)"
+ ",master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",reserve_balance_val INT8 NOT NULL"
+ ",reserve_balance_frac INT4 NOT NULL"
+ ",reserve_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",withdraw_fee_balance_val INT8 NOT NULL"
+ ",withdraw_fee_balance_frac INT4 NOT NULL"
+ ",withdraw_fee_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",expiration_date INT8 NOT NULL"
+ ",auditor_reserves_rowid BIGSERIAL"
+ ")"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX auditor_reserves_by_reserve_pub "
+ "ON auditor_reserves(reserve_pub)"),
+ /* Table with the sum of the balances of all customer reserves
+ (by exchange's master public key) */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS
auditor_reserve_balance"
+ "(master_pub BYTEA PRIMARY KEY CHECK
(LENGTH(master_pub)=32)"
+ ",reserve_balance_val INT8 NOT NULL"
+ ",reserve_balance_frac INT4 NOT NULL"
+ ",reserve_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",withdraw_fee_balance_val INT8 NOT NULL"
+ ",withdraw_fee_balance_frac INT4 NOT NULL"
+ ",withdraw_fee_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")"),
+ /* Table with the sum of the balances of all wire fees
+ (by exchange's master public key) */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS
auditor_wire_fee_balance"
+ "(master_pub BYTEA PRIMARY KEY CHECK
(LENGTH(master_pub)=32)"
+ ",wire_fee_balance_val INT8 NOT NULL"
+ ",wire_fee_balance_frac INT4 NOT NULL"
+ ",wire_fee_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")"),
+ /* Table with all of the outstanding denomination coins that the
+ exchange is aware of. "last_deposit_serial_id" marks the
+ deposit_serial_id from "deposits" about this denomination key
+ that the auditor is aware of; "last_melt_serial_id" marks the
+ last melt from "refresh_sessions" that the auditor is aware
+ of; "refund_serial_id" tells us the last entry in "refunds"
+ for this denom_pub that the auditor is aware of. */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS denomination_pending"
+ "(denom_pub_hash BYTEA PRIMARY KEY"
+ /* " REFERENCES auditor_denominations
(denom_pub_hash) ON DELETE CASCADE" // Do we want this? */
+ ",denom_balance_val INT8 NOT NULL"
+ ",denom_balance_frac INT4 NOT NULL"
+ ",denom_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",denom_risk_val INT8 NOT NULL"
+ ",denom_risk_frac INT4 NOT NULL"
+ ",denom_risk_curr VARCHAR("TALER_CURRENCY_LEN_STR")
NOT NULL"
+ ")"),
+ /* Table with the sum of the outstanding coins from
+ "denomination_pending" (denom_pubs must belong to the
+ respective's exchange's master public key); it represents the
+ balance_summary of the exchange at this point (modulo
+ unexpected historic_loss-style events where denomination keys are
+ compromised) */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS balance_summary"
+ "(master_pub BYTEA PRIMARY KEY CHECK
(LENGTH(master_pub)=32)"
+ ",denom_balance_val INT8 NOT NULL"
+ ",denom_balance_frac INT4 NOT NULL"
+ ",denom_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",deposit_fee_balance_val INT8 NOT NULL"
+ ",deposit_fee_balance_frac INT4 NOT NULL"
+ ",deposit_fee_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",melt_fee_balance_val INT8 NOT NULL"
+ ",melt_fee_balance_frac INT4 NOT NULL"
+ ",melt_fee_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",refund_fee_balance_val INT8 NOT NULL"
+ ",refund_fee_balance_frac INT4 NOT NULL"
+ ",refund_fee_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",risk_val INT8 NOT NULL"
+ ",risk_frac INT4 NOT NULL"
+ ",risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
+ ")"),
+ /* Table with historic profits; basically, when a denom_pub has
+ expired and everything associated with it is garbage collected,
+ the final profits end up in here; note that the "denom_pub" here
+ is not a foreign key, we just keep it as a reference point.
+ "revenue_balance" is the sum of all of the profits we made on the
+ coin except for withdraw fees (which are in
+ historic_reserve_revenue); the deposit, melt and refund fees are given
+ individually; the delta to the revenue_balance is from coins that
+ were withdrawn but never deposited prior to expiration. */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS
historic_denomination_revenue"
+ "(master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",denom_pub_hash BYTEA PRIMARY KEY CHECK
(LENGTH(denom_pub_hash)=64)"
+ ",revenue_timestamp INT8 NOT NULL"
+ ",revenue_balance_val INT8 NOT NULL"
+ ",revenue_balance_frac INT4 NOT NULL"
+ ",revenue_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")"),
+ /* Table with historic losses; basically, when we need to
+ invalidate a denom_pub because the denom_priv was
+ compromised, we incur a loss. These losses are totaled
+ up here. (NOTE: the 'bankrupcy' protocol is not yet
+ implemented, so right now this table is not used.) */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS historic_losses"
+ "(master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",denom_pub_hash BYTEA PRIMARY KEY CHECK
(LENGTH(denom_pub_hash)=64)"
+ ",loss_timestamp INT8 NOT NULL"
+ ",loss_balance_val INT8 NOT NULL"
+ ",loss_balance_frac INT4 NOT NULL"
+ ",loss_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")"),
+ /* Table with historic profits from reserves; we eventually
+ GC "historic_reserve_revenue", and then store the totals
+ in here (by time intervals). */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS
historic_reserve_summary"
+ "(master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",start_date INT8 NOT NULL"
+ ",end_date INT8 NOT NULL"
+ ",reserve_profits_val INT8 NOT NULL"
+ ",reserve_profits_frac INT4 NOT NULL"
+ ",reserve_profits_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX
historic_reserve_summary_by_master_pub_start_date "
+ "ON
historic_reserve_summary(master_pub,start_date)"),
+ /* Table with historic business ledger; basically, when the exchange
+ operator decides to use operating costs for anything but wire
+ transfers to merchants, it goes in here. This happens when the
+ operator users transaction fees for business expenses. "purpose"
+ is free-form but should be a human-readable wire transfer
+ identifier. This is NOT yet used and outside of the scope of
+ the core auditing logic. However, once we do take fees to use
+ operating costs, and if we still want "predicted_result" to match
+ the tables overall, we'll need a command-line tool to insert rows
+ into this table and update "predicted_result" accordingly.
+ (So this table for now just exists as a reminder of what we'll
+ need in the long term.) */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS historic_ledger"
+ "(master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",purpose VARCHAR NOT NULL"
+ ",timestamp INT8 NOT NULL"
+ ",balance_val INT8 NOT NULL"
+ ",balance_frac INT4 NOT NULL"
+ ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR")
NOT NULL"
+ ")"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX
history_ledger_by_master_pub_and_time "
+ "ON historic_ledger(master_pub,timestamp)"),
+ /* Table with the sum of the ledger, historic_revenue,
+ historic_losses and the auditor_reserve_balance. This is the
+ final amount that the exchange should have in its bank account
+ right now. */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS predicted_result"
+ "(master_pub BYTEA PRIMARY KEY CHECK
(LENGTH(master_pub)=32)"
+ ",balance_val INT8 NOT NULL"
+ ",balance_frac INT4 NOT NULL"
+ ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR")
NOT NULL"
+ ")"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
PGconn *conn;
+ int ret;
conn = connect_to_postgres (pc);
if (NULL == conn)
return GNUNET_SYSERR;
-#define SQLEXEC(sql) SQLEXEC_(conn, sql);
-#define SQLEXEC_INDEX(sql) SQLEXEC_IGNORE_ERROR_(conn, sql);
-
- /* Table with all of the denomination keys that the auditor
- is aware of. */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_denominations"
- "(denom_pub_hash BYTEA PRIMARY KEY CHECK
(LENGTH(denom_pub_hash)=64)"
- ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",valid_from INT8 NOT NULL"
- ",expire_withdraw INT8 NOT NULL"
- ",expire_deposit INT8 NOT NULL"
- ",expire_legal INT8 NOT NULL"
- ",coin_val INT8 NOT NULL" /* value of this denom */
- ",coin_frac INT4 NOT NULL" /* fractional value of this denom */
- ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" /* assuming
same currency for fees */
- ",fee_withdraw_val INT8 NOT NULL"
- ",fee_withdraw_frac INT4 NOT NULL"
- ",fee_withdraw_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",fee_deposit_val INT8 NOT NULL"
- ",fee_deposit_frac INT4 NOT NULL"
- ",fee_deposit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",fee_refresh_val INT8 NOT NULL"
- ",fee_refresh_frac INT4 NOT NULL"
- ",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",fee_refund_val INT8 NOT NULL"
- ",fee_refund_frac INT4 NOT NULL"
- ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- /* Table indicating up to which transactions the auditor has
- processed the exchange database. Used for SELECTing the
- statements to process. We basically trace the exchange's
- operations by the 6 primary tables: reserves_in,
- reserves_out, deposits, refresh_sessions, refunds and prewire. The
- other tables of the exchange DB just provide supporting
- evidence which is checked alongside the audit of these
- five tables. The 6 indices below include the last serial
- ID from the respective tables that we have processed. Thus,
- we need to select those table entries that are strictly
- larger (and process in monotonically increasing order). */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_progress"
- "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
- ",last_reserve_in_serial_id INT8 NOT NULL"
- ",last_reserve_out_serial_id INT8 NOT NULL"
- ",last_reserve_payback_serial_id INT8 NOT NULL"
- ",last_reserve_close_serial_id INT8 NOT NULL"
- ",last_withdraw_serial_id INT8 NOT NULL"
- ",last_deposit_serial_id INT8 NOT NULL"
- ",last_melt_serial_id INT8 NOT NULL"
- ",last_refund_serial_id INT8 NOT NULL"
- ",last_wire_out_serial_id INT8 NOT NULL"
- ")");
-
- /* Table with all of the customer reserves and their respective
- balances that the auditor is aware of.
- "last_reserve_out_serial_id" marks the last withdrawal from
- "reserves_out" about this reserve that the auditor is aware of,
- and "last_reserve_in_serial_id" is the last "reserve_in"
- operation about this reserve that the auditor is aware of. */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_reserves"
- "(reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32)"
- ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",reserve_balance_val INT8 NOT NULL"
- ",reserve_balance_frac INT4 NOT NULL"
- ",reserve_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",withdraw_fee_balance_val INT8 NOT NULL"
- ",withdraw_fee_balance_frac INT4 NOT NULL"
- ",withdraw_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
- ",expiration_date INT8 NOT NULL"
- ",auditor_reserves_rowid BIGSERIAL"
- ")");
-
- SQLEXEC_INDEX("CREATE INDEX auditor_reserves_by_reserve_pub "
- "ON auditor_reserves(reserve_pub)");
-
- /* Table with the sum of the balances of all customer reserves
- (by exchange's master public key) */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_reserve_balance"
- "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
- ",reserve_balance_val INT8 NOT NULL"
- ",reserve_balance_frac INT4 NOT NULL"
- ",reserve_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",withdraw_fee_balance_val INT8 NOT NULL"
- ",withdraw_fee_balance_frac INT4 NOT NULL"
- ",withdraw_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
- ")");
-
- /* Table with the sum of the balances of all wire fees
- (by exchange's master public key) */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_wire_fee_balance"
- "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
- ",wire_fee_balance_val INT8 NOT NULL"
- ",wire_fee_balance_frac INT4 NOT NULL"
- ",wire_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- /* Table with all of the outstanding denomination coins that the
- exchange is aware of. "last_deposit_serial_id" marks the
- deposit_serial_id from "deposits" about this denomination key
- that the auditor is aware of; "last_melt_serial_id" marks the
- last melt from "refresh_sessions" that the auditor is aware
- of; "refund_serial_id" tells us the last entry in "refunds"
- for this denom_pub that the auditor is aware of. */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS denomination_pending"
- "(denom_pub_hash BYTEA PRIMARY KEY"
- /* " REFERENCES auditor_denominations (denom_pub_hash) ON DELETE
CASCADE" // Do we want this? */
- ",denom_balance_val INT8 NOT NULL"
- ",denom_balance_frac INT4 NOT NULL"
- ",denom_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",denom_risk_val INT8 NOT NULL"
- ",denom_risk_frac INT4 NOT NULL"
- ",denom_risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- /* Table with the sum of the outstanding coins from
- "denomination_pending" (denom_pubs must belong to the
- respective's exchange's master public key); it represents the
- balance_summary of the exchange at this point (modulo
- unexpected historic_loss-style events where denomination keys are
- compromised) */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS balance_summary"
- "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
- ",denom_balance_val INT8 NOT NULL"
- ",denom_balance_frac INT4 NOT NULL"
- ",denom_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",deposit_fee_balance_val INT8 NOT NULL"
- ",deposit_fee_balance_frac INT4 NOT NULL"
- ",deposit_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
- ",melt_fee_balance_val INT8 NOT NULL"
- ",melt_fee_balance_frac INT4 NOT NULL"
- ",melt_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",refund_fee_balance_val INT8 NOT NULL"
- ",refund_fee_balance_frac INT4 NOT NULL"
- ",refund_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
- ",risk_val INT8 NOT NULL"
- ",risk_frac INT4 NOT NULL"
- ",risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
-
- /* Table with historic profits; basically, when a denom_pub has
- expired and everything associated with it is garbage collected,
- the final profits end up in here; note that the "denom_pub" here
- is not a foreign key, we just keep it as a reference point.
- "revenue_balance" is the sum of all of the profits we made on the
- coin except for withdraw fees (which are in
- historic_reserve_revenue); the deposit, melt and refund fees are given
- individually; the delta to the revenue_balance is from coins that
- were withdrawn but never deposited prior to expiration. */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_denomination_revenue"
- "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)"
- ",revenue_timestamp INT8 NOT NULL"
- ",revenue_balance_val INT8 NOT NULL"
- ",revenue_balance_frac INT4 NOT NULL"
- ",revenue_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
-
- /* Table with historic losses; basically, when we need to
- invalidate a denom_pub because the denom_priv was
- compromised, we incur a loss. These losses are totaled
- up here. (NOTE: the 'bankrupcy' protocol is not yet
- implemented, so right now this table is not used.) */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_losses"
- "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)"
- ",loss_timestamp INT8 NOT NULL"
- ",loss_balance_val INT8 NOT NULL"
- ",loss_balance_frac INT4 NOT NULL"
- ",loss_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- /* Table with historic profits from reserves; we eventually
- GC "historic_reserve_revenue", and then store the totals
- in here (by time intervals). */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_reserve_summary"
- "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",start_date INT8 NOT NULL"
- ",end_date INT8 NOT NULL"
- ",reserve_profits_val INT8 NOT NULL"
- ",reserve_profits_frac INT4 NOT NULL"
- ",reserve_profits_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- SQLEXEC_INDEX("CREATE INDEX
historic_reserve_summary_by_master_pub_start_date "
- "ON historic_reserve_summary(master_pub,start_date)");
-
-
- /* Table with historic business ledger; basically, when the exchange
- operator decides to use operating costs for anything but wire
- transfers to merchants, it goes in here. This happens when the
- operator users transaction fees for business expenses. "purpose"
- is free-form but should be a human-readable wire transfer
- identifier. This is NOT yet used and outside of the scope of
- the core auditing logic. However, once we do take fees to use
- operating costs, and if we still want "predicted_result" to match
- the tables overall, we'll need a command-line tool to insert rows
- into this table and update "predicted_result" accordingly.
- (So this table for now just exists as a reminder of what we'll
- need in the long term.) */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_ledger"
- "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",purpose VARCHAR NOT NULL"
- ",timestamp INT8 NOT NULL"
- ",balance_val INT8 NOT NULL"
- ",balance_frac INT4 NOT NULL"
- ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- SQLEXEC_INDEX("CREATE INDEX history_ledger_by_master_pub_and_time "
- "ON historic_ledger(master_pub,timestamp)");
-
- /* Table with the sum of the ledger, historic_revenue,
- historic_losses and the auditor_reserve_balance. This is the
- final amount that the exchange should have in its bank account
- right now. */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS predicted_result"
- "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
- ",balance_val INT8 NOT NULL"
- ",balance_frac INT4 NOT NULL"
- ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
-
-#undef SQLEXEC
-#undef SQLEXEC_INDEX
-
- PQfinish (conn);
- return GNUNET_OK;
-
- SQLEXEC_fail:
+ ret = GNUNET_PQ_exec_statements (conn,
+ es);
PQfinish (conn);
- return GNUNET_SYSERR;
+ return ret;
}
@@ -503,445 +407,408 @@ postgres_create_tables (void *cls)
static int
postgres_prepare (PGconn *db_conn)
{
- PGresult *result;
-
-#define PREPARE(name, sql, ...) \
- do { \
- result = PQprepare (db_conn, name, sql, __VA_ARGS__); \
- if (PGRES_COMMAND_OK != PQresultStatus (result)) \
- { \
- BREAK_DB_ERR (result); \
- PQclear (result); result = NULL; \
- return GNUNET_SYSERR; \
- } \
- PQclear (result); result = NULL; \
- } while (0);
-
- /* Used in #postgres_insert_denomination_info() */
- PREPARE ("auditor_denominations_insert",
- "INSERT INTO auditor_denominations "
- "(denom_pub_hash"
- ",master_pub"
- ",valid_from"
- ",expire_withdraw"
- ",expire_deposit"
- ",expire_legal"
- ",coin_val"
- ",coin_frac"
- ",coin_curr"
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_withdraw_curr"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_deposit_curr"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refresh_curr"
- ",fee_refund_val"
- ",fee_refund_frac"
- ",fee_refund_curr"
- ") VALUES
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);",
- 21, NULL);
-
- /* Used in #postgres_insert_denomination_info() */
- PREPARE ("auditor_denominations_select",
- "SELECT"
- " denom_pub_hash"
- ",valid_from"
- ",expire_withdraw"
- ",expire_deposit"
- ",expire_legal"
- ",coin_val"
- ",coin_frac"
- ",coin_curr"
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_withdraw_curr"
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_deposit_curr"
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refresh_curr"
- ",fee_refund_val"
- ",fee_refund_frac"
- ",fee_refund_curr"
- " FROM auditor_denominations"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_auditor_progress() */
- PREPARE ("auditor_progress_insert",
- "INSERT INTO auditor_progress "
- "(master_pub"
- ",last_reserve_in_serial_id"
- ",last_reserve_out_serial_id"
- ",last_reserve_payback_serial_id"
- ",last_reserve_close_serial_id"
- ",last_withdraw_serial_id"
- ",last_deposit_serial_id"
- ",last_melt_serial_id"
- ",last_refund_serial_id"
- ",last_wire_out_serial_id"
- ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
- 10, NULL);
-
- /* Used in #postgres_update_auditor_progress() */
- PREPARE ("auditor_progress_update",
- "UPDATE auditor_progress SET "
- " last_reserve_in_serial_id=$1"
- ",last_reserve_out_serial_id=$2"
- ",last_reserve_payback_serial_id=$3"
- ",last_reserve_close_serial_id=$4"
- ",last_withdraw_serial_id=$5"
- ",last_deposit_serial_id=$6"
- ",last_melt_serial_id=$7"
- ",last_refund_serial_id=$8"
- ",last_wire_out_serial_id=$9"
- " WHERE master_pub=$10",
- 10, NULL);
-
- /* Used in #postgres_get_auditor_progress() */
- PREPARE ("auditor_progress_select",
- "SELECT"
- " last_reserve_in_serial_id"
- ",last_reserve_out_serial_id"
- ",last_reserve_payback_serial_id"
- ",last_reserve_close_serial_id"
- ",last_withdraw_serial_id"
- ",last_deposit_serial_id"
- ",last_melt_serial_id"
- ",last_refund_serial_id"
- ",last_wire_out_serial_id"
- " FROM auditor_progress"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_reserve_info() */
- PREPARE ("auditor_reserves_insert",
- "INSERT INTO auditor_reserves "
- "(reserve_pub"
- ",master_pub"
- ",reserve_balance_val"
- ",reserve_balance_frac"
- ",reserve_balance_curr"
- ",withdraw_fee_balance_val"
- ",withdraw_fee_balance_frac"
- ",withdraw_fee_balance_curr"
- ",expiration_date"
- ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
- 9, NULL);
-
- /* Used in #postgres_update_reserve_info() */
- PREPARE ("auditor_reserves_update",
- "UPDATE auditor_reserves SET"
- " reserve_balance_val=$1"
- ",reserve_balance_frac=$2"
- ",reserve_balance_curr=$3"
- ",withdraw_fee_balance_val=$4"
- ",withdraw_fee_balance_frac=$5"
- ",withdraw_fee_balance_curr=$6"
- ",expiration_date=$7"
- " WHERE reserve_pub=$8 AND master_pub=$9;",
- 9, NULL);
-
- /* Used in #postgres_get_reserve_info() */
- PREPARE ("auditor_reserves_select",
- "SELECT"
- " reserve_balance_val"
- ",reserve_balance_frac"
- ",reserve_balance_curr"
- ",withdraw_fee_balance_val"
- ",withdraw_fee_balance_frac"
- ",withdraw_fee_balance_curr"
- ",expiration_date"
- ",auditor_reserves_rowid"
- " FROM auditor_reserves"
- " WHERE reserve_pub=$1 AND master_pub=$2;",
- 2, NULL);
-
- /* Used in #postgres_del_reserve_info() */
- PREPARE ("auditor_reserves_delete",
- "DELETE"
- " FROM auditor_reserves"
- " WHERE reserve_pub=$1 AND master_pub=$2;",
- 2, NULL);
-
- /* Used in #postgres_insert_reserve_summary() */
- PREPARE ("auditor_reserve_balance_insert",
- "INSERT INTO auditor_reserve_balance"
- "(master_pub"
- ",reserve_balance_val"
- ",reserve_balance_frac"
- ",reserve_balance_curr"
- ",withdraw_fee_balance_val"
- ",withdraw_fee_balance_frac"
- ",withdraw_fee_balance_curr"
- ") VALUES ($1,$2,$3,$4,$5,$6,$7)",
- 7, NULL);
-
- /* Used in #postgres_update_reserve_summary() */
- PREPARE ("auditor_reserve_balance_update",
- "UPDATE auditor_reserve_balance SET"
- " reserve_balance_val=$1"
- ",reserve_balance_frac=$2"
- ",reserve_balance_curr=$3"
- ",withdraw_fee_balance_val=$4"
- ",withdraw_fee_balance_frac=$5"
- ",withdraw_fee_balance_curr=$6"
- " WHERE master_pub=$7;",
- 7, NULL);
-
- /* Used in #postgres_get_reserve_summary() */
- PREPARE ("auditor_reserve_balance_select",
- "SELECT"
- " reserve_balance_val"
- ",reserve_balance_frac"
- ",reserve_balance_curr"
- ",withdraw_fee_balance_val"
- ",withdraw_fee_balance_frac"
- ",withdraw_fee_balance_curr"
- " FROM auditor_reserve_balance"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_wire_fee_summary() */
- PREPARE ("auditor_wire_fee_balance_insert",
- "INSERT INTO auditor_wire_fee_balance"
- "(master_pub"
- ",wire_fee_balance_val"
- ",wire_fee_balance_frac"
- ",wire_fee_balance_curr"
- ") VALUES ($1,$2,$3,$4)",
- 4, NULL);
-
- /* Used in #postgres_update_wire_fee_summary() */
- PREPARE ("auditor_wire_fee_balance_update",
- "UPDATE auditor_wire_fee_balance SET"
- " wire_fee_balance_val=$1"
- ",wire_fee_balance_frac=$2"
- ",wire_fee_balance_curr=$3"
- " WHERE master_pub=$4;",
- 4, NULL);
-
- /* Used in #postgres_get_wire_fee_summary() */
- PREPARE ("auditor_wire_fee_balance_select",
- "SELECT"
- " wire_fee_balance_val"
- ",wire_fee_balance_frac"
- ",wire_fee_balance_curr"
- " FROM auditor_wire_fee_balance"
- " WHERE master_pub=$1;",
- 1, NULL);
-
-
- /* Used in #postgres_insert_denomination_balance() */
- PREPARE ("denomination_pending_insert",
- "INSERT INTO denomination_pending "
- "(denom_pub_hash"
- ",denom_balance_val"
- ",denom_balance_frac"
- ",denom_balance_curr"
- ",denom_risk_val"
- ",denom_risk_frac"
- ",denom_risk_curr"
- ") VALUES ($1,$2,$3,$4,$5,$6,$7);",
- 7, NULL);
-
- /* Used in #postgres_update_denomination_balance() */
- PREPARE ("denomination_pending_update",
- "UPDATE denomination_pending SET"
- " denom_balance_val=$1"
- ",denom_balance_frac=$2"
- ",denom_balance_curr=$3"
- ",denom_risk_val=$4"
- ",denom_risk_frac=$5"
- ",denom_risk_curr=$6"
- " WHERE denom_pub_hash=$7",
- 7, NULL);
-
- /* Used in #postgres_get_denomination_balance() */
- PREPARE ("denomination_pending_select",
- "SELECT"
- " denom_balance_val"
- ",denom_balance_frac"
- ",denom_balance_curr"
- ",denom_risk_val"
- ",denom_risk_frac"
- ",denom_risk_curr"
- " FROM denomination_pending"
- " WHERE denom_pub_hash=$1",
- 1, NULL);
-
- /* Used in #postgres_insert_balance_summary() */
- PREPARE ("balance_summary_insert",
- "INSERT INTO balance_summary "
- "(master_pub"
- ",denom_balance_val"
- ",denom_balance_frac"
- ",denom_balance_curr"
- ",deposit_fee_balance_val"
- ",deposit_fee_balance_frac"
- ",deposit_fee_balance_curr"
- ",melt_fee_balance_val"
- ",melt_fee_balance_frac"
- ",melt_fee_balance_curr"
- ",refund_fee_balance_val"
- ",refund_fee_balance_frac"
- ",refund_fee_balance_curr"
- ",risk_val"
- ",risk_frac"
- ",risk_curr"
- ") VALUES
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);",
- 16, NULL);
-
- /* Used in #postgres_update_balance_summary() */
- PREPARE ("balance_summary_update",
- "UPDATE balance_summary SET"
- " denom_balance_val=$1"
- ",denom_balance_frac=$2"
- ",denom_balance_curr=$3"
- ",deposit_fee_balance_val=$4"
- ",deposit_fee_balance_frac=$5"
- ",deposit_fee_balance_curr=$6"
- ",melt_fee_balance_val=$7"
- ",melt_fee_balance_frac=$8"
- ",melt_fee_balance_curr=$9"
- ",refund_fee_balance_val=$10"
- ",refund_fee_balance_frac=$11"
- ",refund_fee_balance_curr=$12"
- ",risk_val=$13"
- ",risk_frac=$14"
- ",risk_curr=$15"
- " WHERE master_pub=$16;",
- 16, NULL);
-
- /* Used in #postgres_get_balance_summary() */
- PREPARE ("balance_summary_select",
- "SELECT"
- " denom_balance_val"
- ",denom_balance_frac"
- ",denom_balance_curr"
- ",deposit_fee_balance_val"
- ",deposit_fee_balance_frac"
- ",deposit_fee_balance_curr"
- ",melt_fee_balance_val"
- ",melt_fee_balance_frac"
- ",melt_fee_balance_curr"
- ",refund_fee_balance_val"
- ",refund_fee_balance_frac"
- ",refund_fee_balance_curr"
- ",risk_val"
- ",risk_frac"
- ",risk_curr"
- " FROM balance_summary"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_historic_denom_revenue() */
- PREPARE ("historic_denomination_revenue_insert",
- "INSERT INTO historic_denomination_revenue"
- "(master_pub"
- ",denom_pub_hash"
- ",revenue_timestamp"
- ",revenue_balance_val"
- ",revenue_balance_frac"
- ",revenue_balance_curr"
- ") VALUES ($1,$2,$3,$4,$5,$6);",
- 6, NULL);
-
- /* Used in #postgres_select_historic_denom_revenue() */
- PREPARE ("historic_denomination_revenue_select",
- "SELECT"
- " denom_pub_hash"
- ",revenue_timestamp"
- ",revenue_balance_val"
- ",revenue_balance_frac"
- ",revenue_balance_curr"
- " FROM historic_denomination_revenue"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_historic_losses() */
- PREPARE ("historic_losses_insert",
- "INSERT INTO historic_losses"
- "(master_pub"
- ",denom_pub_hash"
- ",loss_timestamp"
- ",loss_balance_val"
- ",loss_balance_frac"
- ",loss_balance_curr"
- ") VALUES ($1,$2,$3,$4,$5,$6);",
- 6, NULL);
-
- /* Used in #postgres_select_historic_losses() */
- PREPARE ("historic_losses_select",
- "SELECT"
- " denom_pub_hash"
- ",loss_timestamp"
- ",loss_balance_val"
- ",loss_balance_frac"
- ",loss_balance_curr"
- " FROM historic_losses"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_historic_reserve_revenue() */
- PREPARE ("historic_reserve_summary_insert",
- "INSERT INTO historic_reserve_summary"
- "(master_pub"
- ",start_date"
- ",end_date"
- ",reserve_profits_val"
- ",reserve_profits_frac"
- ",reserve_profits_curr"
- ") VALUES ($1,$2,$3,$4,$5,$6);",
- 6, NULL);
-
- /* Used in #postgres_select_historic_reserve_revenue() */
- PREPARE ("historic_reserve_summary_select",
- "SELECT"
- " start_date"
- ",end_date"
- ",reserve_profits_val"
- ",reserve_profits_frac"
- ",reserve_profits_curr"
- " FROM historic_reserve_summary"
- " WHERE master_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_predicted_result() */
- PREPARE ("predicted_result_insert",
- "INSERT INTO predicted_result"
- "(master_pub"
- ",balance_val"
- ",balance_frac"
- ",balance_curr"
- ") VALUES ($1,$2,$3,$4);",
- 4, NULL);
-
- /* Used in #postgres_update_predicted_result() */
- PREPARE ("predicted_result_update",
- "UPDATE predicted_result SET"
- " balance_val=$1"
- ",balance_frac=$2"
- ",balance_curr=$3"
- " WHERE master_pub=$4;",
- 4, NULL);
-
- /* Used in #postgres_get_predicted_balance() */
- PREPARE ("predicted_result_select",
- "SELECT"
- " balance_val"
- ",balance_frac"
- ",balance_curr"
- " FROM predicted_result"
- " WHERE master_pub=$1;",
- 1, NULL);
+ struct GNUNET_PQ_PreparedStatement ps[] = {
+ /* used in #postgres_commit */
+ GNUNET_PQ_make_prepare ("do_commit",
+ "COMMIT",
+ 0),
+ /* Used in #postgres_insert_denomination_info() */
+ GNUNET_PQ_make_prepare ("auditor_denominations_insert",
+ "INSERT INTO auditor_denominations "
+ "(denom_pub_hash"
+ ",master_pub"
+ ",valid_from"
+ ",expire_withdraw"
+ ",expire_deposit"
+ ",expire_legal"
+ ",coin_val"
+ ",coin_frac"
+ ",coin_curr"
+ ",fee_withdraw_val"
+ ",fee_withdraw_frac"
+ ",fee_withdraw_curr"
+ ",fee_deposit_val"
+ ",fee_deposit_frac"
+ ",fee_deposit_curr"
+ ",fee_refresh_val"
+ ",fee_refresh_frac"
+ ",fee_refresh_curr"
+ ",fee_refund_val"
+ ",fee_refund_frac"
+ ",fee_refund_curr"
+ ") VALUES
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);",
+ 21),
+ /* Used in #postgres_insert_denomination_info() */
+ GNUNET_PQ_make_prepare ("auditor_denominations_select",
+ "SELECT"
+ " denom_pub_hash"
+ ",valid_from"
+ ",expire_withdraw"
+ ",expire_deposit"
+ ",expire_legal"
+ ",coin_val"
+ ",coin_frac"
+ ",coin_curr"
+ ",fee_withdraw_val"
+ ",fee_withdraw_frac"
+ ",fee_withdraw_curr"
+ ",fee_deposit_val"
+ ",fee_deposit_frac"
+ ",fee_deposit_curr"
+ ",fee_refresh_val"
+ ",fee_refresh_frac"
+ ",fee_refresh_curr"
+ ",fee_refund_val"
+ ",fee_refund_frac"
+ ",fee_refund_curr"
+ " FROM auditor_denominations"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_auditor_progress() */
+ GNUNET_PQ_make_prepare ("auditor_progress_insert",
+ "INSERT INTO auditor_progress "
+ "(master_pub"
+ ",last_reserve_in_serial_id"
+ ",last_reserve_out_serial_id"
+ ",last_reserve_payback_serial_id"
+ ",last_reserve_close_serial_id"
+ ",last_withdraw_serial_id"
+ ",last_deposit_serial_id"
+ ",last_melt_serial_id"
+ ",last_refund_serial_id"
+ ",last_wire_out_serial_id"
+ ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
+ 10),
+ /* Used in #postgres_update_auditor_progress() */
+ GNUNET_PQ_make_prepare ("auditor_progress_update",
+ "UPDATE auditor_progress SET "
+ " last_reserve_in_serial_id=$1"
+ ",last_reserve_out_serial_id=$2"
+ ",last_reserve_payback_serial_id=$3"
+ ",last_reserve_close_serial_id=$4"
+ ",last_withdraw_serial_id=$5"
+ ",last_deposit_serial_id=$6"
+ ",last_melt_serial_id=$7"
+ ",last_refund_serial_id=$8"
+ ",last_wire_out_serial_id=$9"
+ " WHERE master_pub=$10",
+ 10),
+ /* Used in #postgres_get_auditor_progress() */
+ GNUNET_PQ_make_prepare ("auditor_progress_select",
+ "SELECT"
+ " last_reserve_in_serial_id"
+ ",last_reserve_out_serial_id"
+ ",last_reserve_payback_serial_id"
+ ",last_reserve_close_serial_id"
+ ",last_withdraw_serial_id"
+ ",last_deposit_serial_id"
+ ",last_melt_serial_id"
+ ",last_refund_serial_id"
+ ",last_wire_out_serial_id"
+ " FROM auditor_progress"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_reserve_info() */
+ GNUNET_PQ_make_prepare ("auditor_reserves_insert",
+ "INSERT INTO auditor_reserves "
+ "(reserve_pub"
+ ",master_pub"
+ ",reserve_balance_val"
+ ",reserve_balance_frac"
+ ",reserve_balance_curr"
+ ",withdraw_fee_balance_val"
+ ",withdraw_fee_balance_frac"
+ ",withdraw_fee_balance_curr"
+ ",expiration_date"
+ ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
+ 9),
+ /* Used in #postgres_update_reserve_info() */
+ GNUNET_PQ_make_prepare ("auditor_reserves_update",
+ "UPDATE auditor_reserves SET"
+ " reserve_balance_val=$1"
+ ",reserve_balance_frac=$2"
+ ",reserve_balance_curr=$3"
+ ",withdraw_fee_balance_val=$4"
+ ",withdraw_fee_balance_frac=$5"
+ ",withdraw_fee_balance_curr=$6"
+ ",expiration_date=$7"
+ " WHERE reserve_pub=$8 AND master_pub=$9;",
+ 9),
+ /* Used in #postgres_get_reserve_info() */
+ GNUNET_PQ_make_prepare ("auditor_reserves_select",
+ "SELECT"
+ " reserve_balance_val"
+ ",reserve_balance_frac"
+ ",reserve_balance_curr"
+ ",withdraw_fee_balance_val"
+ ",withdraw_fee_balance_frac"
+ ",withdraw_fee_balance_curr"
+ ",expiration_date"
+ ",auditor_reserves_rowid"
+ " FROM auditor_reserves"
+ " WHERE reserve_pub=$1 AND master_pub=$2;",
+ 2),
+ /* Used in #postgres_del_reserve_info() */
+ GNUNET_PQ_make_prepare ("auditor_reserves_delete",
+ "DELETE"
+ " FROM auditor_reserves"
+ " WHERE reserve_pub=$1 AND master_pub=$2;",
+ 2),
+ /* Used in #postgres_insert_reserve_summary() */
+ GNUNET_PQ_make_prepare ("auditor_reserve_balance_insert",
+ "INSERT INTO auditor_reserve_balance"
+ "(master_pub"
+ ",reserve_balance_val"
+ ",reserve_balance_frac"
+ ",reserve_balance_curr"
+ ",withdraw_fee_balance_val"
+ ",withdraw_fee_balance_frac"
+ ",withdraw_fee_balance_curr"
+ ") VALUES ($1,$2,$3,$4,$5,$6,$7)",
+ 7),
+ /* Used in #postgres_update_reserve_summary() */
+ GNUNET_PQ_make_prepare ("auditor_reserve_balance_update",
+ "UPDATE auditor_reserve_balance SET"
+ " reserve_balance_val=$1"
+ ",reserve_balance_frac=$2"
+ ",reserve_balance_curr=$3"
+ ",withdraw_fee_balance_val=$4"
+ ",withdraw_fee_balance_frac=$5"
+ ",withdraw_fee_balance_curr=$6"
+ " WHERE master_pub=$7;",
+ 7),
+ /* Used in #postgres_get_reserve_summary() */
+ GNUNET_PQ_make_prepare ("auditor_reserve_balance_select",
+ "SELECT"
+ " reserve_balance_val"
+ ",reserve_balance_frac"
+ ",reserve_balance_curr"
+ ",withdraw_fee_balance_val"
+ ",withdraw_fee_balance_frac"
+ ",withdraw_fee_balance_curr"
+ " FROM auditor_reserve_balance"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_wire_fee_summary() */
+ GNUNET_PQ_make_prepare ("auditor_wire_fee_balance_insert",
+ "INSERT INTO auditor_wire_fee_balance"
+ "(master_pub"
+ ",wire_fee_balance_val"
+ ",wire_fee_balance_frac"
+ ",wire_fee_balance_curr"
+ ") VALUES ($1,$2,$3,$4)",
+ 4),
+ /* Used in #postgres_update_wire_fee_summary() */
+ GNUNET_PQ_make_prepare ("auditor_wire_fee_balance_update",
+ "UPDATE auditor_wire_fee_balance SET"
+ " wire_fee_balance_val=$1"
+ ",wire_fee_balance_frac=$2"
+ ",wire_fee_balance_curr=$3"
+ " WHERE master_pub=$4;",
+ 4),
+ /* Used in #postgres_get_wire_fee_summary() */
+ GNUNET_PQ_make_prepare ("auditor_wire_fee_balance_select",
+ "SELECT"
+ " wire_fee_balance_val"
+ ",wire_fee_balance_frac"
+ ",wire_fee_balance_curr"
+ " FROM auditor_wire_fee_balance"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_denomination_balance() */
+ GNUNET_PQ_make_prepare ("denomination_pending_insert",
+ "INSERT INTO denomination_pending "
+ "(denom_pub_hash"
+ ",denom_balance_val"
+ ",denom_balance_frac"
+ ",denom_balance_curr"
+ ",denom_risk_val"
+ ",denom_risk_frac"
+ ",denom_risk_curr"
+ ") VALUES ($1,$2,$3,$4,$5,$6,$7);",
+ 7),
+ /* Used in #postgres_update_denomination_balance() */
+ GNUNET_PQ_make_prepare ("denomination_pending_update",
+ "UPDATE denomination_pending SET"
+ " denom_balance_val=$1"
+ ",denom_balance_frac=$2"
+ ",denom_balance_curr=$3"
+ ",denom_risk_val=$4"
+ ",denom_risk_frac=$5"
+ ",denom_risk_curr=$6"
+ " WHERE denom_pub_hash=$7",
+ 7),
+ /* Used in #postgres_get_denomination_balance() */
+ GNUNET_PQ_make_prepare ("denomination_pending_select",
+ "SELECT"
+ " denom_balance_val"
+ ",denom_balance_frac"
+ ",denom_balance_curr"
+ ",denom_risk_val"
+ ",denom_risk_frac"
+ ",denom_risk_curr"
+ " FROM denomination_pending"
+ " WHERE denom_pub_hash=$1",
+ 1),
+ /* Used in #postgres_insert_balance_summary() */
+ GNUNET_PQ_make_prepare ("balance_summary_insert",
+ "INSERT INTO balance_summary "
+ "(master_pub"
+ ",denom_balance_val"
+ ",denom_balance_frac"
+ ",denom_balance_curr"
+ ",deposit_fee_balance_val"
+ ",deposit_fee_balance_frac"
+ ",deposit_fee_balance_curr"
+ ",melt_fee_balance_val"
+ ",melt_fee_balance_frac"
+ ",melt_fee_balance_curr"
+ ",refund_fee_balance_val"
+ ",refund_fee_balance_frac"
+ ",refund_fee_balance_curr"
+ ",risk_val"
+ ",risk_frac"
+ ",risk_curr"
+ ") VALUES
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);",
+ 16),
+ /* Used in #postgres_update_balance_summary() */
+ GNUNET_PQ_make_prepare ("balance_summary_update",
+ "UPDATE balance_summary SET"
+ " denom_balance_val=$1"
+ ",denom_balance_frac=$2"
+ ",denom_balance_curr=$3"
+ ",deposit_fee_balance_val=$4"
+ ",deposit_fee_balance_frac=$5"
+ ",deposit_fee_balance_curr=$6"
+ ",melt_fee_balance_val=$7"
+ ",melt_fee_balance_frac=$8"
+ ",melt_fee_balance_curr=$9"
+ ",refund_fee_balance_val=$10"
+ ",refund_fee_balance_frac=$11"
+ ",refund_fee_balance_curr=$12"
+ ",risk_val=$13"
+ ",risk_frac=$14"
+ ",risk_curr=$15"
+ " WHERE master_pub=$16;",
+ 16),
+ /* Used in #postgres_get_balance_summary() */
+ GNUNET_PQ_make_prepare ("balance_summary_select",
+ "SELECT"
+ " denom_balance_val"
+ ",denom_balance_frac"
+ ",denom_balance_curr"
+ ",deposit_fee_balance_val"
+ ",deposit_fee_balance_frac"
+ ",deposit_fee_balance_curr"
+ ",melt_fee_balance_val"
+ ",melt_fee_balance_frac"
+ ",melt_fee_balance_curr"
+ ",refund_fee_balance_val"
+ ",refund_fee_balance_frac"
+ ",refund_fee_balance_curr"
+ ",risk_val"
+ ",risk_frac"
+ ",risk_curr"
+ " FROM balance_summary"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_historic_denom_revenue() */
+ GNUNET_PQ_make_prepare ("historic_denomination_revenue_insert",
+ "INSERT INTO historic_denomination_revenue"
+ "(master_pub"
+ ",denom_pub_hash"
+ ",revenue_timestamp"
+ ",revenue_balance_val"
+ ",revenue_balance_frac"
+ ",revenue_balance_curr"
+ ") VALUES ($1,$2,$3,$4,$5,$6);",
+ 6),
+ /* Used in #postgres_select_historic_denom_revenue() */
+ GNUNET_PQ_make_prepare ("historic_denomination_revenue_select",
+ "SELECT"
+ " denom_pub_hash"
+ ",revenue_timestamp"
+ ",revenue_balance_val"
+ ",revenue_balance_frac"
+ ",revenue_balance_curr"
+ " FROM historic_denomination_revenue"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_historic_losses() */
+ GNUNET_PQ_make_prepare ("historic_losses_insert",
+ "INSERT INTO historic_losses"
+ "(master_pub"
+ ",denom_pub_hash"
+ ",loss_timestamp"
+ ",loss_balance_val"
+ ",loss_balance_frac"
+ ",loss_balance_curr"
+ ") VALUES ($1,$2,$3,$4,$5,$6);",
+ 6),
+ /* Used in #postgres_select_historic_losses() */
+ GNUNET_PQ_make_prepare ("historic_losses_select",
+ "SELECT"
+ " denom_pub_hash"
+ ",loss_timestamp"
+ ",loss_balance_val"
+ ",loss_balance_frac"
+ ",loss_balance_curr"
+ " FROM historic_losses"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_historic_reserve_revenue() */
+ GNUNET_PQ_make_prepare ("historic_reserve_summary_insert",
+ "INSERT INTO historic_reserve_summary"
+ "(master_pub"
+ ",start_date"
+ ",end_date"
+ ",reserve_profits_val"
+ ",reserve_profits_frac"
+ ",reserve_profits_curr"
+ ") VALUES ($1,$2,$3,$4,$5,$6);",
+ 6),
+ /* Used in #postgres_select_historic_reserve_revenue() */
+ GNUNET_PQ_make_prepare ("historic_reserve_summary_select",
+ "SELECT"
+ " start_date"
+ ",end_date"
+ ",reserve_profits_val"
+ ",reserve_profits_frac"
+ ",reserve_profits_curr"
+ " FROM historic_reserve_summary"
+ " WHERE master_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_predicted_result() */
+ GNUNET_PQ_make_prepare ("predicted_result_insert",
+ "INSERT INTO predicted_result"
+ "(master_pub"
+ ",balance_val"
+ ",balance_frac"
+ ",balance_curr"
+ ") VALUES ($1,$2,$3,$4);",
+ 4),
+ /* Used in #postgres_update_predicted_result() */
+ GNUNET_PQ_make_prepare ("predicted_result_update",
+ "UPDATE predicted_result SET"
+ " balance_val=$1"
+ ",balance_frac=$2"
+ ",balance_curr=$3"
+ " WHERE master_pub=$4;",
+ 4),
+ /* Used in #postgres_get_predicted_balance() */
+ GNUNET_PQ_make_prepare ("predicted_result_select",
+ "SELECT"
+ " balance_val"
+ ",balance_frac"
+ ",balance_curr"
+ " FROM predicted_result"
+ " WHERE master_pub=$1;",
+ 1),
+ GNUNET_PQ_PREPARED_STATEMENT_END
+ };
- return GNUNET_OK;
-#undef PREPARE
+ return GNUNET_PQ_prepare_statements (db_conn,
+ ps);
}
@@ -954,8 +821,11 @@ static void
db_conn_destroy (void *cls)
{
struct TALER_AUDITORDB_Session *session = cls;
- PGconn *db_conn = session->conn;
+ PGconn *db_conn;
+ if (NULL == session)
+ return;
+ db_conn = session->conn;
if (NULL != db_conn)
PQfinish (db_conn);
GNUNET_free (session);
@@ -977,7 +847,23 @@ postgres_get_session (void *cls)
struct TALER_AUDITORDB_Session *session;
if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
- return session;
+ {
+ if (CONNECTION_BAD == PQstatus (session->conn))
+ {
+ /**
+ * Reset the thread-local database-handle. Disconnects from the
+ * DB. Needed after the database server restarts as we need to
+ * properly reconnect. */
+ GNUNET_assert (0 == pthread_setspecific (pc->db_conn_threadlocal,
+ NULL));
+ PQfinish (session->conn);
+ GNUNET_free (session);
+ }
+ else
+ {
+ return session;
+ }
+ }
db_conn = connect_to_postgres (pc);
if (NULL == db_conn)
return NULL;
@@ -1026,7 +912,6 @@ postgres_start (void *cls,
PQclear (result);
return GNUNET_SYSERR;
}
-
PQclear (result);
return GNUNET_OK;
}
@@ -1058,49 +943,19 @@ postgres_rollback (void *cls,
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session the database connection
- * @return #GNUNET_OK on success
+ * @return transaction status code
*/
-static int
+enum GNUNET_DB_QueryStatus
postgres_commit (void *cls,
struct TALER_AUDITORDB_Session *session)
{
- PGresult *result;
-
- result = PQexec (session->conn,
- "COMMIT");
- if (PGRES_COMMAND_OK !=
- PQresultStatus (result))
- {
- const char *sqlstate;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_end
+ };
- sqlstate = PQresultErrorField (result,
- PG_DIAG_SQLSTATE);
- if (NULL == sqlstate)
- {
- /* very unexpected... */
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- /* 40P01: deadlock, 40001: serialization failure */
- if ( (0 == strcmp (sqlstate,
- "40P01")) ||
- (0 == strcmp (sqlstate,
- "40001")) )
- {
- /* These two can be retried and have a fair chance of working
- the next time */
- PQclear (result);
- return GNUNET_NO;
- }
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "Database commit failure: %s\n",
- sqlstate);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "do_commit",
+ params);
}
@@ -1122,7 +977,7 @@ postgres_gc (void *cls)
GNUNET_PQ_query_param_end
};
PGconn *conn;
- PGresult *result;
+ enum GNUNET_DB_QueryStatus qs;
now = GNUNET_TIME_absolute_get ();
conn = connect_to_postgres (pc);
@@ -1135,17 +990,15 @@ postgres_gc (void *cls)
return GNUNET_SYSERR;
}
/* FIXME: this is obviously not going to be this easy... */
- result = GNUNET_PQ_exec_prepared (conn,
- "gc_auditor",
- params_time);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
+ qs = GNUNET_PQ_eval_prepared_non_select (conn,
+ "gc_auditor",
+ params_time);
+ if (0 > qs)
{
- BREAK_DB_ERR (result);
- PQclear (result);
+ GNUNET_break (0);
PQfinish (conn);
return GNUNET_SYSERR;
}
- PQclear (result);
PQfinish (conn);
return GNUNET_OK;
}
@@ -1159,16 +1012,13 @@ postgres_gc (void *cls)
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param issue issuing information with value, fees and other info about the
denomination
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return operation status result
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_denomination_info (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct
TALER_DenominationKeyValidityPS *issue)
{
- PGresult *result;
- int ret;
-
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&issue->denom_hash),
GNUNET_PQ_query_param_auto_from_type (&issue->master),
@@ -1197,70 +1047,61 @@ postgres_insert_denomination_info (void *cls,
GNUNET_assert (GNUNET_YES ==
TALER_amount_cmp_currency_nbo (&issue->value,
&issue->fee_refund));
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_denominations_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_denominations_insert",
+ params);
}
/**
- * Get information about denomination keys of a particular exchange.
+ * Closure for #denomination_info_cb().
+ */
+struct DenominationInfoContext
+{
+
+ /**
+ * Master public key that is being used.
+ */
+ const struct TALER_MasterPublicKeyP *master_pub;
+
+ /**
+ * Function to call for each denomination.
+ */
+ TALER_AUDITORDB_DenominationInfoDataCallback cb;
+
+ /**
+ * Closure for @e cb
+ */
+ void *cb_cls;
+
+ /**
+ * Query status to return.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_denomination_info().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
*
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param session connection to use
- * @param master_pub master public key of the exchange
- * @param cb function to call with the results
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @param cls closure of type `struct DenominationInfoContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
*/
-static int
-postgres_select_denomination_info (void *cls,
- struct TALER_AUDITORDB_Session *session,
- const struct TALER_MasterPublicKeyP
*master_pub,
-
TALER_AUDITORDB_DenominationInfoDataCallback cb,
- void *cb_cls)
+static void
+denomination_info_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (master_pub),
- GNUNET_PQ_query_param_end
- };
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_denominations_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
+ struct DenominationInfoContext *dic = cls;
- int ret = GNUNET_OK;
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_select_denomination_info() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i = 0; i < nrows; i++)
+ for (unsigned int i = 0; i < num_results; i++)
{
- struct TALER_DenominationKeyValidityPS issue = { .master = *master_pub };
-
+ struct TALER_DenominationKeyValidityPS issue = {
+ .master = *dic->master_pub
+ };
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
&issue.denom_hash),
GNUNET_PQ_result_spec_auto_from_type ("valid_from", &issue.start),
@@ -1274,26 +1115,62 @@ postgres_select_denomination_info (void *cls,
TALER_PQ_result_spec_amount_nbo ("fee_refund", &issue.fee_refund),
GNUNET_PQ_result_spec_end
};
+
if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- ret = cb (cb_cls,
- &issue);
- switch (ret)
- {
- case GNUNET_OK:
- break;
-
- default:
- i = nrows;
+ dic->qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
+ dic->qs = i + 1;
+ if (GNUNET_OK !=
+ dic->cb (dic->cb_cls,
+ &issue))
+ return;
}
- PQclear (result);
- return ret;
+}
+
+
+/**
+ * Get information about denomination keys of a particular exchange.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @param master_pub master public key of the exchange
+ * @param cb function to call with the results
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_denomination_info (void *cls,
+ struct TALER_AUDITORDB_Session *session,
+ const struct TALER_MasterPublicKeyP
*master_pub,
+
TALER_AUDITORDB_DenominationInfoDataCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_end
+ };
+ struct DenominationInfoContext dic = {
+ .master_pub = master_pub,
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "auditor_denominations_select",
+ params,
+ &denomination_info_cb,
+ &dic);
+ if (qs > 0)
+ return dic.qs;
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+ return qs;
}
@@ -1305,15 +1182,14 @@ postgres_select_denomination_info (void *cls,
* @param session connection to use
* @param master_pub master key of the exchange
* @param pp where is the auditor in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-int
+static enum GNUNET_DB_QueryStatus
postgres_insert_auditor_progress (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_AUDITORDB_ProgressPoint
*pp)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
@@ -1327,22 +1203,10 @@ postgres_insert_auditor_progress (void *cls,
GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
GNUNET_PQ_query_param_end
};
- int ret;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_progress_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_progress_insert",
+ params);
}
@@ -1354,15 +1218,14 @@ postgres_insert_auditor_progress (void *cls,
* @param session connection to use
* @param master_pub master key of the exchange
* @param pp where is the auditor in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-int
+static enum GNUNET_DB_QueryStatus
postgres_update_auditor_progress (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_AUDITORDB_ProgressPoint
*pp)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
GNUNET_PQ_query_param_uint64 (&pp->last_reserve_out_serial_id),
@@ -1376,22 +1239,10 @@ postgres_update_auditor_progress (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- int ret;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_progress_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_progress_update",
+ params);
}
@@ -1402,10 +1253,9 @@ postgres_update_auditor_progress (void *cls,
* @param session connection to use
* @param master_pub master key of the exchange
* @param[out] pp set to where the auditor is in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
- * #GNUNET_NO if we have no records for the @a master_pub
+ * @return transaction status code
*/
-int
+static enum GNUNET_DB_QueryStatus
postgres_get_auditor_progress (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -1415,7 +1265,6 @@ postgres_get_auditor_progress (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("last_reserve_in_serial_id",
&pp->last_reserve_in_serial_id),
@@ -1438,38 +1287,10 @@ postgres_get_auditor_progress (void *cls,
GNUNET_PQ_result_spec_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_progress_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_auditor_progress() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "auditor_progress_select",
+ params,
+ rs);
}
@@ -1485,9 +1306,9 @@ postgres_get_auditor_progress (void *cls,
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
* @param expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_reserve_info (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP
*reserve_pub,
@@ -1496,8 +1317,6 @@ postgres_insert_reserve_info (void *cls,
const struct TALER_Amount *withdraw_fee_balance,
struct GNUNET_TIME_Absolute expiration_date)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_auto_from_type (master_pub),
@@ -1511,20 +1330,9 @@ postgres_insert_reserve_info (void *cls,
TALER_amount_cmp_currency (reserve_balance,
withdraw_fee_balance));
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserves_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_reserves_insert",
+ params);
}
@@ -1540,9 +1348,9 @@ postgres_insert_reserve_info (void *cls,
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
* @param expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_update_reserve_info (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP
*reserve_pub,
@@ -1551,8 +1359,6 @@ postgres_update_reserve_info (void *cls,
const struct TALER_Amount *withdraw_fee_balance,
struct GNUNET_TIME_Absolute expiration_date)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (reserve_balance),
TALER_PQ_query_param_amount (withdraw_fee_balance),
@@ -1566,20 +1372,9 @@ postgres_update_reserve_info (void *cls,
TALER_amount_cmp_currency (reserve_balance,
withdraw_fee_balance));
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserves_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_reserves_update",
+ params);
}
@@ -1590,10 +1385,9 @@ postgres_update_reserve_info (void *cls,
* @param session connection to use
* @param reserve_pub public key of the reserve
* @param master_pub master public key of the exchange
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this reserve; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_del_reserve_info (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1604,23 +1398,10 @@ postgres_del_reserve_info (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- int ret;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserves_delete",
- params);
- ret = PQresultStatus (result);
- if (PGRES_COMMAND_OK != ret)
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == strcmp ("0",
- PQcmdTuples (result)))
- return GNUNET_NO;
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_reserves_delete",
+ params);
}
@@ -1636,10 +1417,9 @@ postgres_del_reserve_info (void *cls,
* @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
* @param[out] expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this reserve; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_reserve_info (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1651,31 +1431,10 @@ postgres_get_reserve_info (void *cls,
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserves_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_reserve_info() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("reserve_balance", reserve_balance),
TALER_PQ_result_spec_amount ("withdraw_fee_balance", withdraw_fee_balance),
@@ -1683,15 +1442,11 @@ postgres_get_reserve_info (void *cls,
GNUNET_PQ_result_spec_uint64 ("auditor_reserves_rowid", rowid),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "auditor_reserves_select",
+ params,
+ rs);
}
@@ -1705,17 +1460,15 @@ postgres_get_reserve_info (void *cls,
* @param reserve_balance amount stored in the reserve
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_reserve_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_Amount *reserve_balance,
const struct TALER_Amount
*withdraw_fee_balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
TALER_PQ_query_param_amount (reserve_balance),
@@ -1727,20 +1480,9 @@ postgres_insert_reserve_summary (void *cls,
TALER_amount_cmp_currency (reserve_balance,
withdraw_fee_balance));
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserve_balance_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_reserve_balance_insert",
+ params);
}
@@ -1754,17 +1496,15 @@ postgres_insert_reserve_summary (void *cls,
* @param reserve_balance amount stored in the reserve
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_update_reserve_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_Amount *reserve_balance,
const struct TALER_Amount
*withdraw_fee_balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (reserve_balance),
TALER_PQ_query_param_amount (withdraw_fee_balance),
@@ -1772,20 +1512,9 @@ postgres_update_reserve_summary (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserve_balance_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_reserve_balance_update",
+ params);
}
@@ -1798,10 +1527,9 @@ postgres_update_reserve_summary (void *cls,
* @param[out] reserve_balance amount stored in the reserve
* @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this exchange; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_reserve_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -1812,47 +1540,18 @@ postgres_get_reserve_summary (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_reserve_balance_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_reserve_summary() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("reserve_balance", reserve_balance),
TALER_PQ_result_spec_amount ("withdraw_fee_balance", withdraw_fee_balance),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
-}
-
-
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+
"auditor_reserve_balance_select",
+ params,
+ rs);
+}
/**
@@ -1863,36 +1562,23 @@ postgres_get_reserve_summary (void *cls,
* @param session connection to use
* @param master_pub master public key of the exchange
* @param wire_fee_balance amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_wire_fee_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_Amount *wire_fee_balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
- TALER_PQ_query_param_amount (wire_fee_balance),
+ TALER_PQ_query_param_amount (wire_fee_balance),
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_wire_fee_balance_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_wire_fee_balance_insert",
+ params);
}
@@ -1904,36 +1590,23 @@ postgres_insert_wire_fee_summary (void *cls,
* @param session connection to use
* @param master_pub master public key of the exchange
* @param wire_fee_balance amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_update_wire_fee_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_Amount *wire_fee_balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (wire_fee_balance),
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_wire_fee_balance_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "auditor_wire_fee_balance_update",
+ params);
}
@@ -1944,10 +1617,9 @@ postgres_update_wire_fee_summary (void *cls,
* @param session connection to use
* @param master_pub master public key of the exchange
* @param[out] wire_fee_balance set amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this exchange; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_wire_fee_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -1957,42 +1629,16 @@ postgres_get_wire_fee_summary (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "auditor_wire_fee_balance_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_wire_fee_summary() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("wire_fee_balance", wire_fee_balance),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+
"auditor_wire_fee_balance_select",
+ params,
+ rs);
}
@@ -2005,17 +1651,15 @@ postgres_get_wire_fee_summary (void *cls,
* @param denom_pub_hash hash of the denomination public key
* @param denom_balance value of coins outstanding with this denomination key
* @param denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_denomination_balance (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode
*denom_pub_hash,
const struct TALER_Amount *denom_balance,
const struct TALER_Amount *denom_risk)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
TALER_PQ_query_param_amount (denom_balance),
@@ -2023,20 +1667,9 @@ postgres_insert_denomination_balance (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "denomination_pending_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "denomination_pending_insert",
+ params);
}
@@ -2049,17 +1682,15 @@ postgres_insert_denomination_balance (void *cls,
* @param denom_pub_hash hash of the denomination public key
* @param denom_balance value of coins outstanding with this denomination key
* @param denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_update_denomination_balance (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode
*denom_pub_hash,
const struct TALER_Amount *denom_balance,
const struct TALER_Amount *denom_risk)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (denom_balance),
TALER_PQ_query_param_amount (denom_risk),
@@ -2067,20 +1698,9 @@ postgres_update_denomination_balance (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "denomination_pending_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "denomination_pending_update",
+ params);
}
@@ -2092,9 +1712,9 @@ postgres_update_denomination_balance (void *cls,
* @param denom_pub_hash hash of the denomination public key
* @param[out] denom_balance value of coins outstanding with this denomination
key
* @param[out] denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_NO if no record found,
#GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_denomination_balance (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode
*denom_pub_hash,
@@ -2105,43 +1725,16 @@ postgres_get_denomination_balance (void *cls,
GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "denomination_pending_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_denomination_balance() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("denom_balance", denom_balance),
TALER_PQ_result_spec_amount ("denom_risk", denom_risk),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+
"denomination_pending_select",
+ params,
+ rs);
}
@@ -2157,9 +1750,9 @@ postgres_get_denomination_balance (void *cls,
* @param melt_fee_balance total melt fees collected for this DK
* @param refund_fee_balance total refund fees collected for this DK
* @param risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_balance_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2169,8 +1762,6 @@ postgres_insert_balance_summary (void *cls,
const struct TALER_Amount *refund_fee_balance,
const struct TALER_Amount *risk)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
TALER_PQ_query_param_amount (denom_balance),
@@ -2193,20 +1784,9 @@ postgres_insert_balance_summary (void *cls,
TALER_amount_cmp_currency (denom_balance,
refund_fee_balance));
- result = GNUNET_PQ_exec_prepared (session->conn,
- "balance_summary_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "balance_summary_insert",
+ params);
}
@@ -2222,9 +1802,9 @@ postgres_insert_balance_summary (void *cls,
* @param melt_fee_balance total melt fees collected for this DK
* @param refund_fee_balance total refund fees collected for this DK
* @param risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_update_balance_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2234,8 +1814,6 @@ postgres_update_balance_summary (void *cls,
const struct TALER_Amount *refund_fee_balance,
const struct TALER_Amount *risk)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (denom_balance),
TALER_PQ_query_param_amount (deposit_fee_balance),
@@ -2246,20 +1824,9 @@ postgres_update_balance_summary (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "balance_summary_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "balance_summary_update",
+ params);
}
@@ -2274,10 +1841,9 @@ postgres_update_balance_summary (void *cls,
* @param[out] melt_fee_balance total melt fees collected for this DK
* @param[out] refund_fee_balance total refund fees collected for this DK
* @param[out] risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no entry
- * for this @a master_pub; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_balance_summary (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -2291,29 +1857,6 @@ postgres_get_balance_summary (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "balance_summary_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_balance_summary() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("denom_balance", denom_balance),
TALER_PQ_result_spec_amount ("deposit_fee_balance", deposit_fee_balance),
@@ -2322,15 +1865,11 @@ postgres_get_balance_summary (void *cls,
TALER_PQ_result_spec_amount ("risk", risk),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "balance_summary_select",
+ params,
+ rs);
}
@@ -2346,9 +1885,9 @@ postgres_get_balance_summary (void *cls,
* @param revenue_balance what was the total profit made from
* deposit fees, melting fees, refresh fees
* and coins that were never returned?
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_historic_denom_revenue (void *cls,
struct TALER_AUDITORDB_Session
*session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2356,8 +1895,6 @@ postgres_insert_historic_denom_revenue (void *cls,
struct GNUNET_TIME_Absolute
revenue_timestamp,
const struct TALER_Amount
*revenue_balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
@@ -2366,68 +1903,51 @@ postgres_insert_historic_denom_revenue (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "historic_denomination_revenue_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+
"historic_denomination_revenue_insert",
+ params);
}
/**
- * Obtain all of the historic denomination key revenue
- * of the given @a master_pub.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param session connection to use
- * @param master_pub master key of the exchange
- * @param cb function to call with the results
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * Closure for #historic_denom_revenue_cb().
*/
-static int
-postgres_select_historic_denom_revenue (void *cls,
- struct TALER_AUDITORDB_Session
*session,
- const struct TALER_MasterPublicKeyP
*master_pub,
-
TALER_AUDITORDB_HistoricDenominationRevenueDataCallback cb,
- void *cb_cls)
+struct HistoricDenomRevenueContext
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (master_pub),
- GNUNET_PQ_query_param_end
- };
- PGresult *result;
+ /**
+ * Function to call for each result.
+ */
+ TALER_AUDITORDB_HistoricDenominationRevenueDataCallback cb;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "historic_denomination_revenue_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
- int ret = GNUNET_OK;
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_select_historic_denom_revenue() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i = 0; i < nrows; i++)
+ /**
+ * Number of results processed.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_historic_denom_revenue().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct HistoricRevenueContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+historic_denom_revenue_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct HistoricDenomRevenueContext *hrc = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
{
struct GNUNET_HashCode denom_pub_hash;
struct GNUNET_TIME_Absolute revenue_timestamp;
@@ -2440,28 +1960,62 @@ postgres_select_historic_denom_revenue (void *cls,
};
if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ hrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
- ret = cb (cb_cls,
- &denom_pub_hash,
- revenue_timestamp,
- &revenue_balance);
- switch (ret)
- {
- case GNUNET_OK:
+ hrc->qs = i + 1;
+ if (GNUNET_OK !=
+ hrc->cb (hrc->cb_cls,
+ &denom_pub_hash,
+ revenue_timestamp,
+ &revenue_balance))
break;
-
- default:
- i = nrows;
- }
}
- PQclear (result);
- return ret;
+}
+
+
+/**
+ * Obtain all of the historic denomination key revenue
+ * of the given @a master_pub.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @param master_pub master key of the exchange
+ * @param cb function to call with the results
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_historic_denom_revenue (void *cls,
+ struct TALER_AUDITORDB_Session
*session,
+ const struct TALER_MasterPublicKeyP
*master_pub,
+
TALER_AUDITORDB_HistoricDenominationRevenueDataCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_end
+ };
+ struct HistoricDenomRevenueContext hrc = {
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+
"historic_denomination_revenue_select",
+ params,
+ &historic_denom_revenue_cb,
+ &hrc);
+ if (qs <= 0)
+ return qs;
+ return hrc.qs;
}
@@ -2478,9 +2032,9 @@ postgres_select_historic_denom_revenue (void *cls,
* @param denom_pub_hash hash of the denomination key
* @param loss_timestamp when did this profit get realized
* @param loss_balance what was the total loss
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_historic_losses (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2488,8 +2042,6 @@ postgres_insert_historic_losses (void *cls,
struct GNUNET_TIME_Absolute loss_timestamp,
const struct TALER_Amount *loss_balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
@@ -2498,20 +2050,79 @@ postgres_insert_historic_losses (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "historic_losses_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "historic_losses_insert",
+ params);
+}
+
+
+/**
+ * Closure for #losses_cb.
+ */
+struct LossContext
+{
+ /**
+ * Function to call for each result.
+ */
+ TALER_AUDITORDB_HistoricLossesDataCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code to return.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_historic_denom_revenue().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct HistoricRevenueContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+losses_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LossContext *lctx = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
{
- ret = GNUNET_OK;
+ struct GNUNET_HashCode denom_pub_hash;
+ struct GNUNET_TIME_Absolute loss_timestamp;
+ struct TALER_Amount loss_balance;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &denom_pub_hash),
+ GNUNET_PQ_result_spec_auto_from_type ("loss_timestamp", &loss_timestamp),
+ TALER_PQ_result_spec_amount ("loss_balance", &loss_balance),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ lctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
+ }
+ lctx->qs = i + 1;
+ if (GNUNET_OK !=
+ lctx->cb (lctx->cb_cls,
+ &denom_pub_hash,
+ loss_timestamp,
+ &loss_balance))
+ break;
}
- PQclear (result);
- return ret;
}
@@ -2524,9 +2135,9 @@ postgres_insert_historic_losses (void *cls,
* @param master_pub master key of the exchange
* @param cb function to call with the results
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_select_historic_losses (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2537,65 +2148,20 @@ postgres_select_historic_losses (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "historic_losses_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int ret = GNUNET_OK;
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_select_historic_losses() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i = 0; i < nrows; i++)
- {
- struct GNUNET_HashCode denom_pub_hash;
- struct GNUNET_TIME_Absolute loss_timestamp;
- struct TALER_Amount loss_balance;
-
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &denom_pub_hash),
-
- GNUNET_PQ_result_spec_auto_from_type ("loss_timestamp", &loss_timestamp),
-
- TALER_PQ_result_spec_amount ("loss_balance", &loss_balance),
-
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- ret = cb (cb_cls,
- &denom_pub_hash,
- loss_timestamp,
- &loss_balance);
- switch (ret)
- {
- case GNUNET_OK:
- break;
-
- default:
- i = nrows;
- }
- }
- PQclear (result);
- return ret;
+ struct LossContext lctx = {
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "historic_losses_select",
+ params,
+ &losses_cb,
+ &lctx);
+ if (qs <= 0)
+ return qs;
+ return lctx.qs;
}
@@ -2608,9 +2174,9 @@ postgres_select_historic_losses (void *cls,
* @param start_time beginning of aggregated time interval
* @param end_time end of aggregated time interval
* @param reserve_profits total profits made
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_historic_reserve_revenue (void *cls,
struct TALER_AUDITORDB_Session
*session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2618,8 +2184,6 @@ postgres_insert_historic_reserve_revenue (void *cls,
struct GNUNET_TIME_Absolute end_time,
const struct TALER_Amount
*reserve_profits)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_auto_from_type (&start_time),
@@ -2628,100 +2192,117 @@ postgres_insert_historic_reserve_revenue (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "historic_reserve_summary_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "historic_reserve_summary_insert",
+ params);
}
/**
- * Return information about an exchange's historic revenue from reserves.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param session connection to use
- * @param master_pub master key of the exchange
- * @param cb function to call with results
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * Closure for #historic_reserve_revenue_cb().
*/
-static int
-postgres_select_historic_reserve_revenue (void *cls,
- struct TALER_AUDITORDB_Session
*session,
- const struct TALER_MasterPublicKeyP
*master_pub,
-
TALER_AUDITORDB_HistoricReserveRevenueDataCallback cb,
- void *cb_cls)
+struct HistoricReserveRevenueContext
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (master_pub),
- GNUNET_PQ_query_param_end
- };
- PGresult *result;
+ /**
+ * Function to call for each result.
+ */
+ TALER_AUDITORDB_HistoricReserveRevenueDataCallback cb;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "historic_reserve_summary_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
- int ret = GNUNET_OK;
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_select_historic_reserve_revenue() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i = 0; i < nrows; i++)
+ /**
+ * Number of results processed.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_historic_reserve_revenue().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct HistoricRevenueContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+historic_reserve_revenue_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct HistoricReserveRevenueContext *hrc = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
{
struct GNUNET_TIME_Absolute start_date;
struct GNUNET_TIME_Absolute end_date;
struct TALER_Amount reserve_profits;
-
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("start_date", &start_date),
GNUNET_PQ_result_spec_auto_from_type ("end_date", &end_date),
TALER_PQ_result_spec_amount ("reserve_profits", &reserve_profits),
GNUNET_PQ_result_spec_end
};
+
if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ hrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
- ret = cb (cb_cls,
- start_date,
- end_date,
- &reserve_profits);
- switch (ret)
- {
- case GNUNET_OK:
+ hrc->qs = i + 1;
+ if (GNUNET_OK !=
+ hrc->cb (hrc->cb_cls,
+ start_date,
+ end_date,
+ &reserve_profits))
break;
-
- default:
- i = nrows;
- }
}
- PQclear (result);
- return ret;
+}
+
+
+/**
+ * Return information about an exchange's historic revenue from reserves.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @param master_pub master key of the exchange
+ * @param cb function to call with results
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_historic_reserve_revenue (void *cls,
+ struct TALER_AUDITORDB_Session
*session,
+ const struct TALER_MasterPublicKeyP
*master_pub,
+
TALER_AUDITORDB_HistoricReserveRevenueDataCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+ struct HistoricReserveRevenueContext hrc = {
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "historic_reserve_summary_select",
+ params,
+ &historic_reserve_revenue_cb,
+ &hrc);
+ if (0 >= qs)
+ return qs;
+ return hrc.qs;
}
@@ -2733,36 +2314,23 @@ postgres_select_historic_reserve_revenue (void *cls,
* @param session connection to use
* @param master_pub master key of the exchange
* @param balance what the bank account balance of the exchange should show
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_predicted_result (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_Amount *balance)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
TALER_PQ_query_param_amount (balance),
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "predicted_result_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "predicted_result_insert",
+ params);
}
@@ -2774,37 +2342,23 @@ postgres_insert_predicted_result (void *cls,
* @param session connection to use
* @param master_pub master key of the exchange
* @param balance what the bank account balance of the exchange should show
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_update_predicted_result (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
const struct TALER_Amount *balance)
{
- PGresult *result;
- int ret;
-
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (balance),
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "predicted_result_update",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "predicted_result_update",
+ params);
}
@@ -2815,10 +2369,9 @@ postgres_update_predicted_result (void *cls,
* @param session connection to use
* @param master_pub master key of the exchange
* @param[out] balance expected bank account balance of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
- * #GNUNET_NO if we have no records for the @a master_pub
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_predicted_balance (void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -2828,43 +2381,16 @@ postgres_get_predicted_balance (void *cls,
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "predicted_result_select",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- int nrows = PQntuples (result);
- if (0 == nrows)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "postgres_get_predicted_balance() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows);
-
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_amount ("balance", balance),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "predicted_result_select",
+ params,
+ rs);
}
diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c
index bc23f11..04c6d23 100644
--- a/src/auditordb/test_auditordb.c
+++ b/src/auditordb/test_auditordb.c
@@ -19,6 +19,7 @@
* @author Gabor X Toth
*/
#include "platform.h"
+#include <gnunet/gnunet_db_lib.h>
#include "taler_auditordb_lib.h"
#include "taler_auditordb_plugin.h"
@@ -69,7 +70,9 @@ select_denomination_info_result (void *cls,
{
const struct TALER_DenominationKeyValidityPS *issue1 = cls;
- if (0 != memcmp (issue1, issue2, sizeof (*issue2)))
+ if (0 != memcmp (issue1,
+ issue2,
+ sizeof (*issue2)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"select_denomination_info_result: issue does not match\n");
@@ -193,7 +196,7 @@ run (void *cls)
TALER_amount_hton (&issue.fee_refresh, &fee_refresh);
TALER_amount_hton (&issue.fee_refund, &fee_refund);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_denomination_info (plugin->cls,
session,
&issue));
@@ -201,11 +204,11 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: select_denomination_info\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >=
plugin->select_denomination_info (plugin->cls,
session,
&master_pub,
- select_denomination_info_result,
+ &select_denomination_info_result,
&issue));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -228,7 +231,7 @@ run (void *cls)
.last_wire_out_serial_id = 0
};
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_auditor_progress (plugin->cls,
session,
&master_pub,
@@ -243,7 +246,7 @@ run (void *cls)
pp.last_refund_serial_id++;
pp.last_wire_out_serial_id++;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_auditor_progress (plugin->cls,
session,
&master_pub,
@@ -252,7 +255,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: get_auditor_progress\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_auditor_progress (plugin->cls,
session,
&master_pub,
@@ -277,7 +280,7 @@ run (void *cls)
TALER_string_to_amount (CURRENCY ":23.456789",
&withdraw_fee_balance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_reserve_info (plugin->cls,
session,
&reserve_pub,
@@ -289,7 +292,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: update_reserve_info\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_reserve_info (plugin->cls,
session,
&reserve_pub,
@@ -301,7 +304,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: get_reserve_info\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_reserve_info (plugin->cls,
session,
&reserve_pub,
@@ -318,7 +321,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_reserve_summary\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_reserve_summary (plugin->cls,
session,
&master_pub,
@@ -328,7 +331,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: update_reserve_summary\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_reserve_summary (plugin->cls,
session,
&master_pub,
@@ -341,22 +344,33 @@ run (void *cls)
ZR_BLK (&reserve_balance2);
ZR_BLK (&withdraw_fee_balance2);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_reserve_summary (plugin->cls,
session,
&master_pub,
&reserve_balance2,
&withdraw_fee_balance2));
- FAILIF (0 != memcmp (&reserve_balance2, &reserve_balance, sizeof
(reserve_balance))
- || 0 != memcmp (&withdraw_fee_balance2, &withdraw_fee_balance,
sizeof (withdraw_fee_balance)));
-
+ FAILIF ( (0 != memcmp (&reserve_balance2,
+ &reserve_balance,
+ sizeof (reserve_balance)) ||
+ (0 != memcmp (&withdraw_fee_balance2,
+ &withdraw_fee_balance,
+ sizeof (withdraw_fee_balance))) ) );
+
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_denomination_balance\n");
- struct TALER_Amount denom_balance, deposit_fee_balance, melt_fee_balance,
refund_fee_balance;
- struct TALER_Amount denom_balance2, deposit_fee_balance2, melt_fee_balance2,
refund_fee_balance2;
- struct TALER_Amount rbalance, rbalance2;
+ struct TALER_Amount denom_balance;
+ struct TALER_Amount deposit_fee_balance;
+ struct TALER_Amount melt_fee_balance;
+ struct TALER_Amount refund_fee_balance;
+ struct TALER_Amount denom_balance2;
+ struct TALER_Amount deposit_fee_balance2;
+ struct TALER_Amount melt_fee_balance2;
+ struct TALER_Amount refund_fee_balance2;
+ struct TALER_Amount rbalance;
+ struct TALER_Amount rbalance2;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":12.345678",
@@ -374,7 +388,7 @@ run (void *cls)
TALER_string_to_amount (CURRENCY ":13.57986",
&rbalance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_denomination_balance (plugin->cls,
session,
&denom_pub_hash,
@@ -389,7 +403,7 @@ run (void *cls)
pp.last_melt_serial_id++;
pp.last_refund_serial_id++;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_denomination_balance (plugin->cls,
session,
&denom_pub_hash,
@@ -398,7 +412,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: get_denomination_balance\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_denomination_balance (plugin->cls,
session,
&denom_pub_hash,
@@ -412,7 +426,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_balance_summary\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_balance_summary (plugin->cls,
session,
&master_pub,
@@ -425,7 +439,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: update_balance_summary\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_balance_summary (plugin->cls,
session,
&master_pub,
@@ -444,7 +458,7 @@ run (void *cls)
ZR_BLK (&refund_fee_balance2);
ZR_BLK (&rbalance2);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_balance_summary (plugin->cls,
session,
&master_pub,
@@ -454,17 +468,27 @@ run (void *cls)
&refund_fee_balance2,
&rbalance2));
- FAILIF (0 != memcmp (&denom_balance2, &denom_balance, sizeof (denom_balance))
- || 0 != memcmp (&deposit_fee_balance2, &deposit_fee_balance, sizeof
(deposit_fee_balance))
- || 0 != memcmp (&melt_fee_balance2, &melt_fee_balance, sizeof
(melt_fee_balance))
- || 0 != memcmp (&refund_fee_balance2, &refund_fee_balance, sizeof
(refund_fee_balance)));
- FAILIF (0 != memcmp (&rbalance2, &rbalance, sizeof (rbalance)));
+ FAILIF ( (0 != memcmp (&denom_balance2,
+ &denom_balance,
+ sizeof (denom_balance)) ) ||
+ (0 != memcmp (&deposit_fee_balance2,
+ &deposit_fee_balance,
+ sizeof (deposit_fee_balance)) ) ||
+ (0 != memcmp (&melt_fee_balance2,
+ &melt_fee_balance,
+ sizeof (melt_fee_balance)) ) ||
+ (0 != memcmp (&refund_fee_balance2,
+ &refund_fee_balance,
+ sizeof (refund_fee_balance))) );
+ FAILIF (0 != memcmp (&rbalance2,
+ &rbalance,
+ sizeof (rbalance)));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_historic_denom_revenue\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_historic_denom_revenue (plugin->cls,
session,
&master_pub,
@@ -472,7 +496,7 @@ run (void *cls)
past,
&rbalance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_historic_denom_revenue (plugin->cls,
session,
&master_pub,
@@ -510,17 +534,17 @@ run (void *cls)
return GNUNET_OK;
}
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >=
plugin->select_historic_denom_revenue (plugin->cls,
session,
&master_pub,
-
select_historic_denom_revenue_result,
+
&select_historic_denom_revenue_result,
NULL));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_historic_losses\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_historic_losses (plugin->cls,
session,
&master_pub,
@@ -528,7 +552,7 @@ run (void *cls)
past,
&rbalance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_historic_losses (plugin->cls,
session,
&master_pub,
@@ -567,7 +591,7 @@ run (void *cls)
return GNUNET_OK;
}
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >=
plugin->select_historic_losses (plugin->cls,
session,
&master_pub,
@@ -582,7 +606,7 @@ run (void *cls)
TALER_string_to_amount (CURRENCY ":56.789012",
&reserve_profits));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_historic_reserve_revenue (plugin->cls,
session,
&master_pub,
@@ -590,7 +614,7 @@ run (void *cls)
future,
&reserve_profits));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_historic_reserve_revenue (plugin->cls,
session,
&master_pub,
@@ -627,7 +651,7 @@ run (void *cls)
return GNUNET_OK;
}
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >=
plugin->select_historic_reserve_revenue (plugin->cls,
session,
&master_pub,
@@ -637,7 +661,7 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: insert_predicted_result\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_predicted_result (plugin->cls,
session,
&master_pub,
@@ -650,18 +674,18 @@ run (void *cls)
TALER_string_to_amount (CURRENCY ":78.901234",
&rbalance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_predicted_result (plugin->cls,
session,
&master_pub,
&rbalance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_wire_fee_summary (plugin->cls,
session,
&master_pub,
&rbalance));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->update_wire_fee_summary (plugin->cls,
session,
&master_pub,
@@ -669,7 +693,7 @@ run (void *cls)
{
struct TALER_Amount rprof;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_wire_fee_summary (plugin->cls,
session,
&master_pub,
@@ -678,7 +702,7 @@ run (void *cls)
TALER_amount_cmp (&rprof,
&reserve_profits));
}
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >
plugin->commit (plugin->cls,
session));
@@ -690,13 +714,13 @@ run (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: get_predicted_balance\n");
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_predicted_balance (plugin->cls,
session,
&master_pub,
&rbalance2));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->del_reserve_info (plugin->cls,
session,
&reserve_pub,
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 48c7e9c..d499753 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -11,7 +11,7 @@ lib_LTLIBRARIES = \
libtalerfakebank.la
libtalerbank_la_LDFLAGS = \
- -version-info 0:0:0 \
+ -version-info 1:0:0 \
-no-undefined
libtalerbank_la_SOURCES = \
diff --git a/src/bank-lib/bank_api_common.c b/src/bank-lib/bank_api_common.c
index e68ba41..738d2a5 100644
--- a/src/bank-lib/bank_api_common.c
+++ b/src/bank-lib/bank_api_common.c
@@ -80,13 +80,12 @@ TALER_BANK_make_auth_header_ (const struct
TALER_BANK_AuthenticationData *auth)
authh = append (authh,
"X-Taler-Bank-Password",
auth->details.basic.password);
- break;
+ return authh;
}
- return authh;
+ return NULL;
}
-
/**
* Obtain the URL to use for an API request.
*
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index ceda4d3..2689241 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -65,7 +65,7 @@ struct Transaction
/**
* Subject of the transfer.
*/
- struct TALER_WireTransferIdentifierRawP wtid;
+ char *subject;
/**
* Base URL of the exchange.
@@ -119,7 +119,7 @@ struct TALER_FAKEBANK_Handle
/**
* Check that the @a want_amount was transferred from
* the @a want_debit to the @a want_credit account. If
- * so, set the @a wtid to the transfer identifier.
+ * so, set the @a subject to the transfer identifier.
* If not, return #GNUNET_SYSERR.
*
* @param h bank instance
@@ -128,7 +128,7 @@ struct TALER_FAKEBANK_Handle
* @param want_credit account that should have been credited
* @param exchange_base_url expected base URL of the exchange
* i.e. "https://example.com/"; may include a port
- * @param[out] wtid set to the wire transfer identifier
+ * @param[out] subject set to the wire transfer identifier
* @return #GNUNET_OK on success
*/
int
@@ -137,11 +137,9 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
uint64_t want_debit,
uint64_t want_credit,
const char *exchange_base_url,
- struct TALER_WireTransferIdentifierRawP *wtid)
+ char **subject)
{
- struct Transaction *t;
-
- for (t = h->transactions_head; NULL != t; t = t->next)
+ for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
{
if ( (want_debit == t->debit_account) &&
(want_credit == t->credit_account) &&
@@ -153,7 +151,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
GNUNET_CONTAINER_DLL_remove (h->transactions_head,
h->transactions_tail,
t);
- *wtid = t->wtid;
+ *subject = t->subject;
GNUNET_free (t->exchange_base_url);
GNUNET_free (t);
return GNUNET_OK;
@@ -161,7 +159,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
}
fprintf (stderr,
"Did not find matching transaction!\nI have:\n");
- for (t = h->transactions_head; NULL != t; t = t->next)
+ for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
{
char *s;
@@ -179,6 +177,49 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
/**
+ * Tell the fakebank to create another wire transfer.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param subject wire transfer subject to use
+ * @param exchange_base_url exchange URL
+ * @return serial_id of the transfer
+ */
+uint64_t
+TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
+ uint64_t debit_account,
+ uint64_t credit_account,
+ const struct TALER_Amount *amount,
+ const char *subject,
+ const char *exchange_base_url)
+{
+ struct Transaction *t;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Making transfer from %llu to %llu over %s and subject %s\n",
+ (unsigned long long) debit_account,
+ (unsigned long long) credit_account,
+ TALER_amount2s (amount),
+ subject);
+ t = GNUNET_new (struct Transaction);
+ t->debit_account = debit_account;
+ t->credit_account = credit_account;
+ t->amount = *amount;
+ t->exchange_base_url = GNUNET_strdup (exchange_base_url);
+ t->serial_id = ++h->serial_counter;
+ t->date = GNUNET_TIME_absolute_get ();
+ t->subject = GNUNET_strdup (subject);
+ GNUNET_TIME_round_abs (&t->date);
+ GNUNET_CONTAINER_DLL_insert_tail (h->transactions_head,
+ h->transactions_tail,
+ t);
+ return t->serial_id;
+}
+
+
+/**
* Check that no wire transfers were ordered (or at least none
* that have not been taken care of via #TALER_FAKEBANK_check()).
* If any transactions are onrecord, return #GNUNET_SYSERR.
@@ -228,6 +269,7 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
GNUNET_CONTAINER_DLL_remove (h->transactions_head,
h->transactions_tail,
t);
+ GNUNET_free (t->subject);
GNUNET_free (t->exchange_base_url);
GNUNET_free (t);
}
@@ -291,9 +333,9 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
{
enum GNUNET_JSON_PostResult pr;
json_t *json;
- struct Transaction *t;
struct MHD_Response *resp;
int ret;
+ uint64_t serial_id;
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
con_cls,
@@ -316,14 +358,17 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle
*h,
case GNUNET_JSON_PR_SUCCESS:
break;
}
- t = GNUNET_new (struct Transaction);
{
+ const char *wtid;
+ uint64_t debit_account;
+ uint64_t credit_account;
const char *base_url;
+ struct TALER_Amount amount;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("wtid", &t->wtid),
- GNUNET_JSON_spec_uint64 ("debit_account", &t->debit_account),
- GNUNET_JSON_spec_uint64 ("credit_account", &t->credit_account),
- TALER_JSON_spec_amount ("amount", &t->amount),
+ GNUNET_JSON_spec_string ("wtid", &wtid),
+ GNUNET_JSON_spec_uint64 ("debit_account", &debit_account),
+ GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
+ TALER_JSON_spec_amount ("amount", &amount),
GNUNET_JSON_spec_string ("exchange_url", &base_url),
GNUNET_JSON_spec_end ()
};
@@ -336,19 +381,18 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle
*h,
json_decref (json);
return MHD_NO;
}
- t->exchange_base_url = GNUNET_strdup (base_url);
- t->serial_id = ++h->serial_counter;
- t->date = GNUNET_TIME_absolute_get ();
- GNUNET_TIME_round_abs (&t->date);
- GNUNET_CONTAINER_DLL_insert_tail (h->transactions_head,
- h->transactions_tail,
- t);
+ serial_id = TALER_FAKEBANK_make_transfer (h,
+ debit_account,
+ credit_account,
+ &amount,
+ wtid,
+ base_url);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Receiving incoming wire transfer: %llu->%llu from %s\n",
+ (unsigned long long) debit_account,
+ (unsigned long long) credit_account,
+ base_url);
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %llu->%llu from %s\n",
- (unsigned long long) t->debit_account,
- (unsigned long long) t->credit_account,
- t->exchange_base_url);
json_decref (json);
/* Finally build response object */
@@ -358,7 +402,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
json = json_pack ("{s:I}",
"serial_id",
- (json_int_t) t->serial_id);
+ (json_int_t) serial_id);
json_str = json_dumps (json,
JSON_INDENT(2));
json_decref (json);
@@ -463,6 +507,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
GNUNET_break (0);
return MHD_NO;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Client asked for up to %lld results of type %s for account %llu
starting at %s\n",
+ count,
+ dir,
+ (unsigned long long) account_number,
+ start);
if (NULL == dir)
direction = TALER_BANK_DIRECTION_BOTH;
else if (0 == strcasecmp (dir,
@@ -477,7 +527,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
else
pos = h->transactions_tail;
}
- else
+ else if (NULL != h->transactions_head)
{
for (pos = h->transactions_head;
NULL != pos;
@@ -495,6 +545,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
if (count < 0)
pos = pos->prev;
}
+ else
+ {
+ /* list is empty */
+ pos = NULL;
+ }
+
history = json_array ();
while ( (NULL != pos) &&
(0 != count) )
@@ -502,6 +558,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
json_t *trans;
char *subject;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Found transaction over %s from %llu to %llu\n",
+ TALER_amount2s (&pos->amount),
+ (unsigned long long) pos->debit_account,
+ (unsigned long long) pos->credit_account);
+
if (! ( ( (account_number == pos->debit_account) &&
(0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
( (account_number == pos->credit_account) &&
@@ -514,17 +576,10 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
continue;
}
- {
- char *ws;
-
- ws = GNUNET_STRINGS_data_to_string_alloc (&pos->wtid,
- sizeof (pos->wtid));
- GNUNET_asprintf (&subject,
- "%s %s",
- ws,
- pos->exchange_base_url);
- GNUNET_free (ws);
- }
+ GNUNET_asprintf (&subject,
+ "%s %s",
+ pos->subject,
+ pos->exchange_base_url);
trans = json_pack ("{s:I, s:o, s:o, s:s, s:I, s:s}",
"row_id", (json_int_t) pos->serial_id,
"date", GNUNET_JSON_from_time_abs (pos->date),
@@ -553,6 +608,8 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
struct MHD_Response *resp;
json_decref (history);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning empty transaction history\n");
resp = MHD_create_response_from_buffer (0,
"",
MHD_RESPMEM_PERSISTENT);
diff --git a/src/bank-lib/test_bank_interpreter.c
b/src/bank-lib/test_bank_interpreter.c
index e966424..f5aee8e 100644
--- a/src/bank-lib/test_bank_interpreter.c
+++ b/src/bank-lib/test_bank_interpreter.c
@@ -572,7 +572,9 @@ history_cb (void *cls,
"Expected history of length %llu, got %llu\n",
(unsigned long long) total,
(unsigned long long) cmd->details.history.results_obtained);
- print_expected (h, total, UINT_MAX);
+ print_expected (h,
+ total,
+ UINT_MAX);
free_history (h,
total);
fail (is);
@@ -621,7 +623,6 @@ interpreter_run (void *cls)
struct InterpreterState *is = cls;
struct TBI_Command *cmd = &is->commands[is->ip];
const struct TBI_Command *ref;
- struct TALER_WireTransferIdentifierRawP wtid;
struct TALER_Amount amount;
const struct GNUNET_SCHEDULER_TaskContext *tc;
struct TALER_BANK_AuthenticationData auth;
@@ -719,25 +720,34 @@ interpreter_run (void *cls)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount
(ref->details.admin_add_incoming.amount,
&amount));
- if (GNUNET_OK !=
- TALER_FAKEBANK_check (is->fakebank,
- &amount,
- ref->details.admin_add_incoming.debit_account_no,
-
ref->details.admin_add_incoming.credit_account_no,
-
ref->details.admin_add_incoming.exchange_base_url,
- &wtid))
{
- GNUNET_break (0);
- fail (is);
- return;
- }
- if (0 != memcmp (&wtid,
- &ref->details.admin_add_incoming.wtid,
- sizeof (wtid)))
- {
- GNUNET_break (0);
- fail (is);
- return;
+ char *subject;
+ char *expect;
+
+ if (GNUNET_OK !=
+ TALER_FAKEBANK_check (is->fakebank,
+ &amount,
+
ref->details.admin_add_incoming.debit_account_no,
+
ref->details.admin_add_incoming.credit_account_no,
+
ref->details.admin_add_incoming.exchange_base_url,
+ &subject))
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ expect = GNUNET_STRINGS_data_to_string_alloc
(&ref->details.admin_add_incoming.wtid,
+ sizeof
(ref->details.admin_add_incoming.wtid));
+ if (0 != strcmp (subject, expect))
+ {
+ GNUNET_free (expect);
+ GNUNET_free (subject);
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ GNUNET_free (subject);
+ GNUNET_free (expect);
}
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
diff --git a/src/benchmark/taler-exchange-benchmark.c
b/src/benchmark/taler-exchange-benchmark.c
index 70cc014..10a5eed 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -1234,10 +1234,12 @@ build_refresh ()
*
* @param cls closure
* @param _keys information about keys of the exchange
+ * @param vc compatibility information
*/
static void
cert_cb (void *cls,
- const struct TALER_EXCHANGE_Keys *_keys)
+ const struct TALER_EXCHANGE_Keys *_keys,
+ enum TALER_EXCHANGE_VersionCompatibility vc)
{
/* check that keys is OK */
if (NULL == _keys)
diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index 68cb71e..d891d1d 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -10,7 +10,7 @@ lib_LTLIBRARIES = \
libtalerexchange.la
libtalerexchange_la_LDFLAGS = \
- -version-info 1:0:0 \
+ -version-info 2:0:0 \
-no-undefined
libtalerexchange_la_SOURCES = \
diff --git a/src/exchange-lib/exchange_api_handle.c
b/src/exchange-lib/exchange_api_handle.c
index 590d4de..a03f7d4 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -29,6 +29,17 @@
#include "taler_signatures.h"
#include "exchange_api_handle.h"
+/**
+ * Which revision of the Taler protocol is implemented
+ * by this library? Used to determine compatibility.
+ */
+#define TALER_PROTOCOL_CURRENT 0
+
+/**
+ * How many revisions back are we compatible to?
+ */
+#define TALER_PROTOCOL_AGE 0
+
/**
* Log error related to CURL operations.
@@ -485,11 +496,13 @@ parse_json_auditor (struct
TALER_EXCHANGE_AuditorInformation *auditor,
*
* @param[in] resp_obj JSON object to parse
* @param[out] key_data where to store the results we decoded
+ * @param[out] where to store version compatibility data
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error (malformed JSON)
*/
static int
decode_keys_json (const json_t *resp_obj,
- struct TALER_EXCHANGE_Keys *key_data)
+ struct TALER_EXCHANGE_Keys *key_data,
+ enum TALER_EXCHANGE_VersionCompatibility *vc)
{
struct GNUNET_TIME_Absolute list_issue_date;
struct TALER_ExchangeSignatureP sig;
@@ -497,7 +510,10 @@ decode_keys_json (const json_t *resp_obj,
struct GNUNET_HashContext *hash_context;
struct TALER_ExchangePublicKeyP pub;
- memset (key_data, 0, sizeof (struct TALER_EXCHANGE_Keys));
+ /* FIXME: #4840: handle incremental / cherry-picked /keys! */
+ memset (key_data,
+ 0,
+ sizeof (struct TALER_EXCHANGE_Keys));
if (JSON_OBJECT != json_typeof (resp_obj))
return GNUNET_SYSERR;
@@ -505,6 +521,9 @@ decode_keys_json (const json_t *resp_obj,
/* parse the master public key and issue date of the response */
{
const char *ver;
+ unsigned int age;
+ unsigned int revision;
+ unsigned int current;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("version",
&ver),
@@ -523,6 +542,29 @@ decode_keys_json (const json_t *resp_obj,
GNUNET_JSON_parse (resp_obj,
spec,
NULL, NULL));
+ if (3 != sscanf (ver,
+ "%u:%u:%u",
+ ¤t,
+ &revision,
+ &age))
+ {
+ GNUNET_break_op (0);
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ return GNUNET_SYSERR;
+ }
+ *vc = TALER_EXCHANGE_VC_MATCH;
+ if (TALER_PROTOCOL_CURRENT < current)
+ {
+ *vc |= TALER_EXCHANGE_VC_NEWER;
+ if (TALER_PROTOCOL_CURRENT < current - age)
+ *vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
+ }
+ if (TALER_PROTOCOL_CURRENT > current)
+ {
+ *vc |= TALER_EXCHANGE_VC_OLDER;
+ if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > current)
+ *vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
+ }
key_data->version = GNUNET_strdup (ver);
}
@@ -602,7 +644,8 @@ decode_keys_json (const json_t *resp_obj,
key_data->num_auditors = len;
}
}
-
+ key_data->last_issue_date = list_issue_date;
+
/* Validate signature... */
ks.purpose.size = htonl (sizeof (ks));
ks.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET);
@@ -703,12 +746,14 @@ keys_completed_cb (void *cls,
struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
struct TALER_EXCHANGE_Keys kd;
struct TALER_EXCHANGE_Keys kd_old;
+ enum TALER_EXCHANGE_VersionCompatibility vc;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received keys from URL `%s' with status %ld.\n",
kr->url,
response_code);
kd_old = exchange->key_data;
+ vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
switch (response_code)
{
case 0:
@@ -721,7 +766,8 @@ keys_completed_cb (void *cls,
}
if (GNUNET_OK !=
decode_keys_json (resp_obj,
- &kd))
+ &kd,
+ &vc))
{
response_code = 0;
break;
@@ -744,7 +790,8 @@ keys_completed_cb (void *cls,
exchange->state = MHS_FAILED;
/* notify application that we failed */
exchange->cert_cb (exchange->cert_cb_cls,
- NULL);
+ NULL,
+ vc);
if (NULL != exchange->key_data_raw)
{
json_decref (exchange->key_data_raw);
@@ -760,7 +807,8 @@ keys_completed_cb (void *cls,
exchange->state = MHS_CERT;
/* notify application about the key information */
exchange->cert_cb (exchange->cert_cb_cls,
- &exchange->key_data);
+ &exchange->key_data,
+ vc);
free_key_data (&kd_old);
}
@@ -987,7 +1035,22 @@ request_keys (struct TALER_EXCHANGE_Handle *exchange)
GNUNET_assert (NULL == exchange->kr);
kr = GNUNET_new (struct KeysRequest);
kr->exchange = exchange;
- kr->url = MAH_path_to_url (exchange, "/keys");
+ if (GNUNET_YES == MAH_handle_is_ready (exchange))
+ {
+ char *arg;
+
+ GNUNET_asprintf (&arg,
+ "/keys?last_issue_date=%llu",
+ (unsigned long long)
exchange->key_data.last_issue_date.abs_value_us);
+ kr->url = MAH_path_to_url (exchange,
+ arg);
+ GNUNET_free (arg);
+ }
+ else
+ {
+ kr->url = MAH_path_to_url (exchange,
+ "/keys");
+ }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Requesting keys with URL `%s'.\n",
kr->url);
diff --git a/src/exchange-lib/exchange_api_refresh_link.c
b/src/exchange-lib/exchange_api_refresh_link.c
index 9b82513..5b2286e 100644
--- a/src/exchange-lib/exchange_api_refresh_link.c
+++ b/src/exchange-lib/exchange_api_refresh_link.c
@@ -372,9 +372,9 @@ handle_refresh_link_finished (void *cls,
*/
struct TALER_EXCHANGE_RefreshLinkHandle *
TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
- const struct TALER_CoinSpendPrivateKeyP *coin_priv,
- TALER_EXCHANGE_RefreshLinkCallback link_cb,
- void *link_cb_cls)
+ const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+ TALER_EXCHANGE_RefreshLinkCallback link_cb,
+ void *link_cb_cls)
{
struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
CURL *eh;
diff --git a/src/exchange-lib/test_exchange_api.c
b/src/exchange-lib/test_exchange_api.c
index 53bfbd1..b5b6804 100644
--- a/src/exchange-lib/test_exchange_api.c
+++ b/src/exchange-lib/test_exchange_api.c
@@ -636,9 +636,9 @@ struct Command
const char *exchange_base_url;
/**
- * Set (!) to the wire transfer identifier observed.
+ * Set (!) to the wire transfer subject observed.
*/
- struct TALER_WireTransferIdentifierRawP wtid;
+ char *subject;
} check_bank_transfer;
@@ -1805,18 +1805,24 @@ deposit_wtid_cb (void *cls,
if (NULL != cmd->details.deposit_wtid.bank_transfer_ref)
{
const struct Command *ref;
+ char *ws;
+
+ ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
+ sizeof (*wtid));
+
ref = find_command (is,
cmd->details.deposit_wtid.bank_transfer_ref);
GNUNET_assert (NULL != ref);
- if (0 != memcmp (wtid,
- &ref->details.check_bank_transfer.wtid,
- sizeof (struct TALER_WireTransferIdentifierRawP)))
+ if (0 != strcmp (ws,
+ ref->details.check_bank_transfer.subject))
{
GNUNET_break (0);
+ GNUNET_free (ws);
fail (is);
return;
}
+ GNUNET_free (ws);
}
break;
case MHD_HTTP_ACCEPTED:
@@ -2496,7 +2502,11 @@ interpreter_run (void *cls)
cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid;
break;
case OC_CHECK_BANK_TRANSFER:
- cmd->details.wire_deposits.wtid =
ref->details.check_bank_transfer.wtid;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_STRINGS_string_to_data
(ref->details.check_bank_transfer.subject,
+ strlen
(ref->details.check_bank_transfer.subject),
+
&cmd->details.wire_deposits.wtid,
+ sizeof
(cmd->details.wire_deposits.wtid)));
break;
default:
GNUNET_break (0);
@@ -2597,7 +2607,7 @@ interpreter_run (void *cls)
cmd->details.check_bank_transfer.account_debit,
cmd->details.check_bank_transfer.account_credit,
cmd->details.check_bank_transfer.exchange_base_url,
- &cmd->details.check_bank_transfer.wtid))
+ &cmd->details.check_bank_transfer.subject))
{
GNUNET_break (0);
fail (is);
@@ -2951,6 +2961,8 @@ do_shutdown (void *cls)
}
break;
case OC_CHECK_BANK_TRANSFER:
+ GNUNET_free_non_null (cmd->details.check_bank_transfer.subject);
+ cmd->details.check_bank_transfer.subject = NULL;
break;
case OC_CHECK_BANK_TRANSFERS_EMPTY:
break;
@@ -3038,10 +3050,12 @@ do_shutdown (void *cls)
*
* @param cls closure
* @param keys information about keys of the exchange
+ * @param vc version compatibility
*/
static void
cert_cb (void *cls,
- const struct TALER_EXCHANGE_Keys *keys)
+ const struct TALER_EXCHANGE_Keys *keys,
+ enum TALER_EXCHANGE_VersionCompatibility vc)
{
struct InterpreterState *is = cls;
@@ -3564,7 +3578,7 @@ run (void *cls)
GNUNET_assert (NULL != exchange);
timeout_task
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
- (GNUNET_TIME_UNIT_SECONDS, 150),
+ (GNUNET_TIME_UNIT_SECONDS, 300),
&do_timeout, NULL);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
}
diff --git a/src/exchange/.gitignore b/src/exchange/.gitignore
index 6ea90e7..e54fe49 100644
--- a/src/exchange/.gitignore
+++ b/src/exchange/.gitignore
@@ -5,3 +5,4 @@ taler-exchange-pursemod
taler-exchange-reservemod
taler-exchange-httpd
taler-exchange-wirewatch
+test_taler_exchange_wirewatch-postgres
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 899f396..9ff98d1 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -50,11 +50,15 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
taler-exchange-httpd_parsing.c taler-exchange-httpd_parsing.h \
taler-exchange-httpd_payback.c taler-exchange-httpd_payback.h \
- taler-exchange-httpd_refresh.c taler-exchange-httpd_refresh.h \
+ taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
+ taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
+ taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.h \
taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
- taler-exchange-httpd_reserve.c taler-exchange-httpd_reserve.h \
+ taler-exchange-httpd_reserve_status.c taler-exchange-httpd_reserve_status.h \
+ taler-exchange-httpd_reserve_withdraw.c
taler-exchange-httpd_reserve_withdraw.h \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
- taler-exchange-httpd_tracking.c taler-exchange-httpd_tracking.h \
+ taler-exchange-httpd_track_transaction.c
taler-exchange-httpd_track_transaction.h \
+ taler-exchange-httpd_track_transfer.c taler-exchange-httpd_track_transfer.h \
taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
taler-exchange-httpd_validation.c taler-exchange-httpd_validation.h
taler_exchange_httpd_LDADD = \
@@ -97,8 +101,23 @@ test_taler_exchange_aggregator_postgres_LDADD = \
-ljansson \
-lpthread
+test_taler_exchange_wirewatch_postgres_SOURCES = \
+ test_taler_exchange_wirewatch.c
+test_taler_exchange_wirewatch_postgres_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+ $(top_builddir)/src/bank-lib/libtalerfakebank.la \
+ $(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lmicrohttpd \
+ -lgnunetutil \
+ -lgnunetjson \
+ -ljansson \
+ -lpthread
+
check_PROGRAMS = \
- test_taler_exchange_aggregator-postgres
+ test_taler_exchange_aggregator-postgres \
+ test_taler_exchange_wirewatch-postgres
AM_TESTS_ENVIRONMENT=export
TALER_PREFIX=$${TALER_PREFIX:address@hidden@};export
PATH=$${TALER_PREFIX:address@hidden@}/bin:$$PATH;
@@ -109,6 +128,7 @@ TESTS = \
EXTRA_DIST = \
test-taler-exchange-aggregator-postgres.conf \
+ test-taler-exchange-wirewatch-postgres.conf \
test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv
\
test_taler_exchange_httpd.conf \
exchange.conf \
diff --git a/src/exchange/taler-exchange-aggregator.c
b/src/exchange/taler-exchange-aggregator.c
index f4573aa..fdb32a2 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -196,6 +196,11 @@ struct CloseTransferContext
* Wire transfer method.
*/
char *type;
+
+ /**
+ * Wire plugin used for closing the reserve.
+ */
+ struct WirePlugin *wp;
};
@@ -341,18 +346,19 @@ advance_fees (struct WirePlugin *wp,
* @param wp wire transfer fee data structure to update
* @param now timestamp to update fees to
* @param session DB session to use
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we
- * lack current fee information (and need to exit)
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
update_fees (struct WirePlugin *wp,
struct GNUNET_TIME_Absolute now,
struct TALER_EXCHANGEDB_Session *session)
{
+ enum GNUNET_DB_QueryStatus qs;
+
advance_fees (wp,
now);
if (NULL != wp->af)
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
/* Let's try to load it from disk... */
wp->af = TALER_EXCHANGEDB_fees_read (cfg,
wp->type);
@@ -362,26 +368,26 @@ update_fees (struct WirePlugin *wp,
NULL != p;
p = p->next)
{
- if (GNUNET_SYSERR ==
- db_plugin->insert_wire_fee (db_plugin->cls,
- session,
- wp->type,
- p->start_date,
- p->end_date,
- &p->wire_fee,
- &p->master_sig))
+ qs = db_plugin->insert_wire_fee (db_plugin->cls,
+ session,
+ wp->type,
+ p->start_date,
+ p->end_date,
+ &p->wire_fee,
+ &p->master_sig);
+ if (qs < 0)
{
TALER_EXCHANGEDB_fees_free (wp->af);
wp->af = NULL;
- return GNUNET_SYSERR;
+ return qs;
}
}
if (NULL != wp->af)
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find current wire transfer fees for `%s'\n",
wp->type);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
@@ -420,6 +426,26 @@ find_plugin (const char *type)
return wp;
}
+
+/**
+ * Free data stored in #au.
+ */
+static void
+cleanup_au (void)
+{
+ if (NULL == au)
+ return;
+ GNUNET_free_non_null (au->additional_rows);
+ if (NULL != au->wire)
+ {
+ json_decref (au->wire);
+ au->wire = NULL;
+ }
+ GNUNET_free (au);
+ au = NULL;
+}
+
+
/**
* We're being aborted with CTRL-C (or SIGTERM). Shut down.
*
@@ -428,8 +454,8 @@ find_plugin (const char *type)
static void
shutdown_task (void *cls)
{
- struct WirePlugin *wp;
-
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Running shutdown\n");
if (NULL != task)
{
GNUNET_SCHEDULER_cancel (task);
@@ -458,22 +484,34 @@ shutdown_task (void *cls)
}
db_plugin->rollback (db_plugin->cls,
au->session);
- GNUNET_free_non_null (au->additional_rows);
- if (NULL != au->wire)
- json_decref (au->wire);
- au = NULL;
- GNUNET_free (au);
+ cleanup_au ();
+ }
+ if (NULL != ctc)
+ {
+ ctc->wp->wire_plugin->prepare_wire_transfer_cancel
(ctc->wp->wire_plugin->cls,
+ ctc->ph);
+ ctc->ph = NULL;
+ db_plugin->rollback (db_plugin->cls,
+ ctc->session);
+ GNUNET_free (ctc->type);
+ GNUNET_free (ctc);
+ ctc = NULL;
}
TALER_EXCHANGEDB_plugin_unload (db_plugin);
- while (NULL != (wp = wp_head))
+
{
- GNUNET_CONTAINER_DLL_remove (wp_head,
- wp_tail,
- wp);
- TALER_WIRE_plugin_unload (wp->wire_plugin);
- TALER_EXCHANGEDB_fees_free (wp->af);
- GNUNET_free (wp->type);
- GNUNET_free (wp);
+ struct WirePlugin *wp;
+
+ while (NULL != (wp = wp_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (wp_head,
+ wp_tail,
+ wp);
+ TALER_WIRE_plugin_unload (wp->wire_plugin);
+ TALER_EXCHANGEDB_fees_free (wp->af);
+ GNUNET_free (wp->type);
+ GNUNET_free (wp);
+ }
}
GNUNET_CONFIGURATION_destroy (cfg);
cfg = NULL;
@@ -516,6 +554,14 @@ exchange_serve_process_config ()
"Failed to initialize DB subsystem\n");
return GNUNET_SYSERR;
}
+ if (GNUNET_OK !=
+ db_plugin->create_tables (db_plugin->cls))
+ {
+ fprintf (stderr,
+ "Failed to initialize DB tables\n");
+ TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ return GNUNET_SYSERR;
+ }
return GNUNET_OK;
}
@@ -535,9 +581,9 @@ exchange_serve_process_config ()
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to
continue to iterate
*/
-static int
+static enum GNUNET_DB_QueryStatus
deposit_cb (void *cls,
uint64_t row_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -548,16 +594,19 @@ deposit_cb (void *cls,
struct GNUNET_TIME_Absolute wire_deadline,
const json_t *wire)
{
+ enum GNUNET_DB_QueryStatus qs;
+
au->merchant_pub = *merchant_pub;
- if (GNUNET_OK !=
+ if (GNUNET_SYSERR ==
TALER_amount_subtract (&au->total_amount,
amount_with_fee,
deposit_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Fatally malformed record at row %llu\n",
- (unsigned long long) row_id);
- return GNUNET_SYSERR;
+ "Fatally malformed record at row %llu over %s\n",
+ (unsigned long long) row_id,
+ TALER_amount2s (amount_with_fee));
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
au->row_id = row_id;
GNUNET_assert (NULL == au->wire);
@@ -573,37 +622,41 @@ deposit_cb (void *cls,
au->wp = find_plugin (extract_type (au->wire));
if (NULL == au->wp)
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
/* make sure we have current fees */
au->execution_time = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&au->execution_time);
- if (GNUNET_OK !=
- update_fees (au->wp,
- au->execution_time,
- au->session))
- return GNUNET_SYSERR;
+ qs = update_fees (au->wp,
+ au->execution_time,
+ au->session);
+ if (qs <= 0)
+ {
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
au->wire_fee = au->wp->af->wire_fee;
- if (GNUNET_OK !=
- db_plugin->insert_aggregation_tracking (db_plugin->cls,
- au->session,
- &au->wtid,
- row_id))
+ qs = db_plugin->insert_aggregation_tracking (db_plugin->cls,
+ au->session,
+ &au->wtid,
+ row_id);
+ if (qs <= 0)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- if (GNUNET_OK !=
- db_plugin->mark_deposit_done (db_plugin->cls,
- au->session,
- row_id))
+ qs = db_plugin->mark_deposit_done (db_plugin->cls,
+ au->session,
+ row_id);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
- au->failed = GNUNET_YES;
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- return GNUNET_OK;
+ return qs;
}
@@ -621,9 +674,9 @@ deposit_cb (void *cls,
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
aggregate_cb (void *cls,
uint64_t row_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -635,23 +688,28 @@ aggregate_cb (void *cls,
const json_t *wire)
{
struct TALER_Amount delta;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_break (0 ==
memcmp (&au->merchant_pub,
merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP)));
/* compute contribution of this coin after fees */
- if (GNUNET_OK !=
+ if (GNUNET_SYSERR ==
TALER_amount_subtract (&delta,
amount_with_fee,
deposit_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Fatally malformed record at %llu\n",
- (unsigned long long) row_id);
- return GNUNET_SYSERR;
+ "Fatally malformed record at %llu over amount %s\n",
+ (unsigned long long) row_id,
+ TALER_amount2s (amount_with_fee));
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
/* add to total */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding transaction amount %s to aggregation\n",
+ TALER_amount2s (&delta));
if (GNUNET_OK !=
TALER_amount_add (&au->total_amount,
&au->total_amount,
@@ -661,14 +719,14 @@ aggregate_cb (void *cls,
"Overflow or currency incompatibility during aggregation at
%llu\n",
(unsigned long long) row_id);
/* Skip this one, but keep going! */
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
if (au->rows_offset >= aggregation_limit)
{
/* Bug: we asked for at most #aggregation_limit results! */
GNUNET_break (0);
/* Skip this one, but keep going. */
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
if (NULL == au->additional_rows)
au->additional_rows = GNUNET_new_array (aggregation_limit,
@@ -676,25 +734,27 @@ aggregate_cb (void *cls,
/* "append" to our list of rows */
au->additional_rows[au->rows_offset++] = row_id;
/* insert into aggregation tracking table */
- if (GNUNET_OK !=
- db_plugin->insert_aggregation_tracking (db_plugin->cls,
- au->session,
- &au->wtid,
- row_id))
+ qs = db_plugin->insert_aggregation_tracking (db_plugin->cls,
+ au->session,
+ &au->wtid,
+ row_id);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- if (GNUNET_OK !=
- db_plugin->mark_deposit_done (db_plugin->cls,
- au->session,
- row_id))
+ qs = db_plugin->mark_deposit_done (db_plugin->cls,
+ au->session,
+ row_id);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_break (0);
- au->failed = GNUNET_YES;
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- return GNUNET_OK;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Added row %llu to aggregation\n",
+ (unsigned long long) row_id);
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -733,6 +793,39 @@ run_aggregation (void *cls);
/**
+ * Execute the wire transfers that we have committed to
+ * do.
+ *
+ * @param cls pointer to an `int` which we will return from main()
+ */
+static void
+run_transfers (void *cls);
+
+
+/**
+ * Perform a database commit. If it fails, print a warning.
+ *
+ * @param session session to perform the commit for.
+ * @return status of commit
+ */
+static enum GNUNET_DB_QueryStatus
+commit_or_warn (struct TALER_EXCHANGEDB_Session *session)
+{
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = db_plugin->commit (db_plugin->cls,
+ session);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ return qs;
+ GNUNET_log ((GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ ? GNUNET_ERROR_TYPE_INFO
+ : GNUNET_ERROR_TYPE_ERROR,
+ "Failed to commit database transaction!\n");
+ return qs;
+}
+
+
+/**
* Function to be called with the prepared transfer data
* when closing a reserve.
*
@@ -745,7 +838,12 @@ prepare_close_cb (void *cls,
const char *buf,
size_t buf_size)
{
+ enum GNUNET_DB_QueryStatus qs;
+
GNUNET_assert (cls == ctc);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Prepared for reserve closing\n");
ctc->ph = NULL;
if (NULL == buf)
{
@@ -762,14 +860,25 @@ prepare_close_cb (void *cls,
}
/* Commit our intention to execute the wire transfer! */
- if (GNUNET_OK !=
- db_plugin->wire_prepare_data_insert (db_plugin->cls,
- ctc->session,
- ctc->type,
- buf,
- buf_size))
+ qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
+ ctc->session,
+ ctc->type,
+ buf,
+ buf_size);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ db_plugin->rollback (db_plugin->cls,
+ ctc->session);
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free (ctc->type);
+ GNUNET_free (ctc);
+ ctc = NULL;
+ return;
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
- GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
ctc->session);
/* start again */
@@ -782,48 +891,64 @@ prepare_close_cb (void *cls,
}
/* finally commit */
- if (GNUNET_OK !=
- db_plugin->commit (db_plugin->cls,
- ctc->session))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to commit database transaction!\n");
- }
+ (void) commit_or_warn (ctc->session);
GNUNET_free (ctc->type);
GNUNET_free (ctc);
ctc = NULL;
- task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Reserve closure committed, running transfer\n");
+ task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
}
/**
+ * Closure for #expired_reserve_cb().
+ */
+struct ExpiredReserveContext
+{
+
+ /**
+ * Database session we are using.
+ */
+ struct TALER_EXCHANGEDB_Session *session;
+
+ /**
+ * Set to #GNUNET_YES if the transaction continues
+ * asynchronously.
+ */
+ int async_cont;
+};
+
+
+/**
* Function called with details about expired reserves.
* We trigger the reserve closure by inserting the respective
* closing record and prewire instructions into the respective
* tables.
*
- * @param cls a `struct TALER_EXCHANGEDB_Session *`
+ * @param cls a `struct ExpiredReserveContext *`
* @param reserve_pub public key of the reserve
* @param left amount left in the reserve
* @param account_details information about the reserve's bank account
* @param expiration_date when did the reserve expire
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
expired_reserve_cb (void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *left,
const json_t *account_details,
struct GNUNET_TIME_Absolute expiration_date)
{
- struct TALER_EXCHANGEDB_Session *session = cls;
+ struct ExpiredReserveContext *erc = cls;
+ struct TALER_EXCHANGEDB_Session *session = erc->session;
struct GNUNET_TIME_Absolute now;
struct TALER_WireTransferIdentifierRawP wtid;
struct TALER_Amount amount_without_fee;
const struct TALER_Amount *closing_fee;
int ret;
- int iret;
+ enum GNUNET_DB_QueryStatus qs;
const char *type;
struct WirePlugin *wp;
@@ -835,35 +960,32 @@ expired_reserve_cb (void *cls,
if (NULL == type)
{
GNUNET_break (0);
- db_plugin->rollback (db_plugin->cls,
- session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
wp = find_plugin (type);
if (NULL == wp)
{
GNUNET_break (0);
- db_plugin->rollback (db_plugin->cls,
- session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
/* lookup `closing_fee` */
- if (GNUNET_OK !=
- update_fees (wp,
- now,
- session))
+ qs = update_fees (wp,
+ now,
+ session);
+ if (qs <= 0)
{
- GNUNET_break (0);
- db_plugin->rollback (db_plugin->cls,
- session);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
global_ret = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
- return GNUNET_SYSERR;
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ GNUNET_SCHEDULER_shutdown ();
+ return qs;
}
closing_fee = &wp->af->closing_fee;
@@ -890,16 +1012,22 @@ expired_reserve_cb (void *cls,
reserve_pub,
GNUNET_MIN (sizeof (wtid),
sizeof (*reserve_pub)));
- iret = db_plugin->insert_reserve_closed (db_plugin->cls,
- session,
- reserve_pub,
- now,
- account_details,
- &wtid,
- left,
- closing_fee);
+ qs = db_plugin->insert_reserve_closed (db_plugin->cls,
+ session,
+ reserve_pub,
+ now,
+ account_details,
+ &wtid,
+ left,
+ closing_fee);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Closing reserve %s over %s (%d, %d)\n",
+ TALER_B2S (reserve_pub),
+ TALER_amount2s (left),
+ ret,
+ qs);
if ( (GNUNET_OK == ret) &&
- (GNUNET_OK == iret) )
+ (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) )
{
/* success, perform wire transfer */
if (GNUNET_SYSERR ==
@@ -907,18 +1035,17 @@ expired_reserve_cb (void *cls,
&amount_without_fee))
{
GNUNET_break (0);
- db_plugin->rollback (db_plugin->cls,
- session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
ctc = GNUNET_new (struct CloseTransferContext);
+ ctc->wp = wp;
ctc->session = session;
ctc->type = GNUNET_strdup (type);
ctc->ph
= wp->wire_plugin->prepare_wire_transfer (wp->wire_plugin->cls,
- au->wire,
+ account_details,
&amount_without_fee,
exchange_base_url,
&wtid,
@@ -927,37 +1054,29 @@ expired_reserve_cb (void *cls,
if (NULL == ctc->ph)
{
GNUNET_break (0);
- db_plugin->rollback (db_plugin->cls,
- session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
GNUNET_free (ctc->type);
GNUNET_free (ctc);
ctc = NULL;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
- return GNUNET_SYSERR;
+ erc->async_cont = GNUNET_YES;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
/* Check for hard failure */
- if (GNUNET_SYSERR == iret)
+ if ( (GNUNET_SYSERR == ret) ||
+ (GNUNET_DB_STATUS_HARD_ERROR == qs) )
{
GNUNET_break (0);
- db_plugin->rollback (db_plugin->cls,
- session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
- /* Reserve balance was almost zero; just commit */
- if (GNUNET_OK !=
- db_plugin->commit (db_plugin->cls,
- session))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to commit database transaction!\n");
- }
- task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
- NULL);
- return GNUNET_SYSERR;
+ /* Reserve balance was almost zero OR soft error */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Reserve was virtually empty, moving on\n");
+ return qs;
}
@@ -971,15 +1090,16 @@ static void
run_reserve_closures (void *cls)
{
struct TALER_EXCHANGEDB_Session *session;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
const struct GNUNET_SCHEDULER_TaskContext *tc;
-
+ struct ExpiredReserveContext erc;
+
task = NULL;
reserves_idle = GNUNET_NO;
tc = GNUNET_SCHEDULER_get_task_context ();
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
return;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking for reserves to close\n");
if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
{
@@ -999,28 +1119,44 @@ run_reserve_closures (void *cls)
GNUNET_SCHEDULER_shutdown ();
return;
}
- ret = db_plugin->get_expired_reserves (db_plugin->cls,
- session,
- GNUNET_TIME_absolute_get (),
- &expired_reserve_cb,
- session);
- if (GNUNET_SYSERR == ret)
+ erc.session = session;
+ erc.async_cont = GNUNET_NO;
+ qs = db_plugin->get_expired_reserves (db_plugin->cls,
+ session,
+ GNUNET_TIME_absolute_get (),
+ &expired_reserve_cb,
+ &erc);
+ switch (qs)
{
+ case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
db_plugin->rollback (db_plugin->cls,
session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return;
- }
- if (GNUNET_NO == ret)
- {
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+ NULL);
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No more idle reserves, going back to aggregation\n");
reserves_idle = GNUNET_YES;
db_plugin->rollback (db_plugin->cls,
session);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ if (GNUNET_YES == erc.async_cont)
+ break;
+ (void) commit_or_warn (session);
+ task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+ NULL);
+ return;
}
}
@@ -1036,21 +1172,20 @@ run_aggregation (void *cls)
{
static int swap;
struct TALER_EXCHANGEDB_Session *session;
- unsigned int i;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
const struct GNUNET_SCHEDULER_TaskContext *tc;
task = NULL;
tc = GNUNET_SCHEDULER_get_task_context ();
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
return;
- if (0 == (++swap % 2))
+ if (0 == (++swap % 2))
{
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking for ready deposits to aggregate\n");
if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
{
@@ -1072,19 +1207,16 @@ run_aggregation (void *cls)
}
au = GNUNET_new (struct AggregationUnit);
au->session = session;
- ret = db_plugin->get_ready_deposit (db_plugin->cls,
- session,
- &deposit_cb,
- au);
- if (0 >= ret)
+ qs = db_plugin->get_ready_deposit (db_plugin->cls,
+ session,
+ &deposit_cb,
+ au);
+ if (0 >= qs)
{
- if (NULL != au->wire)
- json_decref (au->wire);
- GNUNET_free (au);
- au = NULL;
+ cleanup_au ();
db_plugin->rollback (db_plugin->cls,
session);
- if (GNUNET_SYSERR == ret)
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to execute deposit iteration!\n");
@@ -1092,20 +1224,31 @@ run_aggregation (void *cls)
GNUNET_SCHEDULER_shutdown ();
return;
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ /* should re-try immediately */
+ swap--; /* do not count failed attempts */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ NULL);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No more ready deposits, going to sleep\n");
- if (GNUNET_YES == test_mode)
+ if ( (GNUNET_YES == test_mode) &&
+ (swap >= 2) )
{
/* in test mode, shutdown if we end up being idle */
GNUNET_SCHEDULER_shutdown ();
}
else
{
- /* nothing to do, sleep for a minute and try again */
- if (GNUNET_NO == reserves_idle)
+ if ( (GNUNET_NO == reserves_idle) ||
+ (GNUNET_YES == test_mode) )
+ /* Possibly more to on reserves, go for it immediately */
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
else
+ /* nothing to do, sleep for a minute and try again */
task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
&run_aggregation,
NULL);
@@ -1117,29 +1260,37 @@ run_aggregation (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Found ready deposit for %s, aggregating\n",
TALER_B2S (&au->merchant_pub));
- ret = db_plugin->iterate_matching_deposits (db_plugin->cls,
- session,
- &au->h_wire,
- &au->merchant_pub,
- &aggregate_cb,
- au,
- aggregation_limit);
- if ( (GNUNET_SYSERR == ret) ||
+ qs = db_plugin->iterate_matching_deposits (db_plugin->cls,
+ session,
+ &au->h_wire,
+ &au->merchant_pub,
+ &aggregate_cb,
+ au,
+ aggregation_limit);
+ if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
(GNUNET_YES == au->failed) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to execute deposit iteration!\n");
- GNUNET_free_non_null (au->additional_rows);
- json_decref (au->wire);
- GNUNET_free (au);
- au = NULL;
+ cleanup_au ();
db_plugin->rollback (db_plugin->cls,
session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return;
}
-
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ /* serializiability issue, try again */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Serialization issue, trying again later!\n");
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ NULL);
+ return;
+ }
+
/* Subtract wire transfer fee and round to the unit supported by the
wire transfer method; Check if after rounding down, we still have
an amount to transfer, and if not mark as 'tiny'. */
@@ -1154,13 +1305,16 @@ run_aggregation (void *cls)
(0 == au->final_amount.fraction) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Aggregate value too low for transfer\n");
+ "Aggregate value too low for transfer (%d/%s)\n",
+ qs,
+ TALER_amount2s (&au->final_amount));
/* Rollback ongoing transaction, as we will not use the respective
WTID and thus need to remove the tracking data */
db_plugin->rollback (db_plugin->cls,
session);
- /* Start another transaction to mark all* of the selected deposits
- *as minor! */
+
+ /* There were results, just the value was too low. Start another
+ transaction to mark all* of the selected deposits as minor! */
if (GNUNET_OK !=
db_plugin->start (db_plugin->cls,
session))
@@ -1168,41 +1322,48 @@ run_aggregation (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction!\n");
global_ret = GNUNET_SYSERR;
+ cleanup_au ();
GNUNET_SCHEDULER_shutdown ();
- GNUNET_free_non_null (au->additional_rows);
- if (NULL != au->wire)
- json_decref (au->wire);
- GNUNET_free (au);
- au = NULL;
return;
}
/* Mark transactions by row_id as minor */
- ret = GNUNET_OK;
- if (GNUNET_OK !=
- db_plugin->mark_deposit_tiny (db_plugin->cls,
- session,
- au->row_id))
- ret = GNUNET_SYSERR;
- else
- for (i=0;i<au->rows_offset;i++)
- if (GNUNET_OK !=
- db_plugin->mark_deposit_tiny (db_plugin->cls,
- session,
- au->additional_rows[i]))
- ret = GNUNET_SYSERR;
- /* commit */
- if (GNUNET_OK !=
- db_plugin->commit (db_plugin->cls,
- session))
+ qs = db_plugin->mark_deposit_tiny (db_plugin->cls,
+ session,
+ au->row_id);
+ if (0 <= qs)
+ {
+ for (unsigned int i=0;i<au->rows_offset;i++)
+ {
+ qs = db_plugin->mark_deposit_tiny (db_plugin->cls,
+ session,
+ au->additional_rows[i]);
+ if (0 > qs)
+ break;
+ }
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Serialization issue, trying again later!\n");
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ cleanup_au ();
+ /* start again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ NULL);
+ return;
+ }
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to commit database transaction!\n");
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ cleanup_au ();
+ GNUNET_SCHEDULER_shutdown ();
+ return;
}
- GNUNET_free_non_null (au->additional_rows);
- if (NULL != au->wire)
- json_decref (au->wire);
- GNUNET_free (au);
- au = NULL;
+ /* commit */
+ (void) commit_or_warn (session);
+ cleanup_au ();
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
@@ -1230,11 +1391,7 @@ run_aggregation (void *cls)
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
session);
- GNUNET_free_non_null (au->additional_rows);
- if (NULL != au->wire)
- json_decref (au->wire);
- GNUNET_free (au);
- au = NULL;
+ cleanup_au ();
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
@@ -1245,16 +1402,6 @@ run_aggregation (void *cls)
/**
- * Execute the wire transfers that we have committed to
- * do.
- *
- * @param cls pointer to an `int` which we will return from main()
- */
-static void
-run_transfers (void *cls);
-
-
-/**
* Function to be called with the prepared transfer data.
*
* @param cls NULL
@@ -1267,8 +1414,10 @@ prepare_cb (void *cls,
size_t buf_size)
{
struct TALER_EXCHANGEDB_Session *session = au->session;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_free_non_null (au->additional_rows);
+ au->additional_rows = NULL;
if (NULL == buf)
{
GNUNET_break (0); /* why? how to best recover? */
@@ -1277,91 +1426,83 @@ prepare_cb (void *cls,
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
- if (NULL != au->wire)
- {
- json_decref (au->wire);
- au->wire = NULL;
- }
- GNUNET_free (au);
- au = NULL;
+ cleanup_au ();
return;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Storing %u bytes of wire prepare data\n",
+ (unsigned int) buf_size);
/* Commit our intention to execute the wire transfer! */
- if (GNUNET_OK !=
- db_plugin->wire_prepare_data_insert (db_plugin->cls,
- session,
- au->wp->type,
- buf,
- buf_size))
+ qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
+ session,
+ au->wp->type,
+ buf,
+ buf_size);
+ /* Commit the WTID data to 'wire_out' to finally satisfy aggregation
+ table constraints */
+ if (qs >= 0)
+ qs = db_plugin->store_wire_transfer_out (db_plugin->cls,
+ session,
+ au->execution_time,
+ &au->wtid,
+ au->wire,
+ &au->final_amount);
+ cleanup_au ();
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
- GNUNET_break (0); /* why? how to best recover? */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Serialization issue for prepared wire data; trying again
later!\n");
db_plugin->rollback (db_plugin->cls,
session);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
- if (NULL != au->wire)
- {
- json_decref (au->wire);
- au->wire = NULL;
- }
- GNUNET_free (au);
- au = NULL;
return;
}
-
- /* Commit the WTID data to 'wire_out' to finally satisfy aggregation
- table constraints */
- if (GNUNET_OK !=
- db_plugin->store_wire_transfer_out (db_plugin->cls,
- session,
- au->execution_time,
- &au->wtid,
- au->wire,
- &au->final_amount))
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
- GNUNET_break (0); /* why? how to best recover? */
+ GNUNET_break (0);
db_plugin->rollback (db_plugin->cls,
session);
- /* start again */
- task = GNUNET_SCHEDULER_add_now (&run_aggregation,
- NULL);
- if (NULL != au->wire)
- {
- json_decref (au->wire);
- au->wire = NULL;
- }
- GNUNET_free (au);
- au = NULL;
+ /* die hard */
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
return;
}
- if (NULL != au->wire)
- {
- json_decref (au->wire);
- au->wire = NULL;
- }
- GNUNET_free (au);
- au = NULL;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Stored wire transfer out instructions\n");
/* Now we can finally commit the overall transaction, as we are
again consistent if all of this passes. */
- if (GNUNET_OK !=
- db_plugin->commit (db_plugin->cls,
- session))
+ switch (commit_or_warn (session))
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Failed to commit database transaction!\n");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
/* try again */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Commit issue for prepared wire data; trying again later!\n");
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Preparation complete, switching to transfer mode\n");
+ /* run alternative task: actually do wire transfer! */
+ task = GNUNET_SCHEDULER_add_now (&run_transfers,
+ NULL);
+ return;
+ default:
+ GNUNET_break (0);
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Preparation complete, switching to transfer mode\n");
- /* run alternative task: actually do wire transfer! */
- task = GNUNET_SCHEDULER_add_now (&run_transfers,
- NULL);
}
@@ -1380,6 +1521,7 @@ wire_confirm_cb (void *cls,
const char *emsg)
{
struct TALER_EXCHANGEDB_Session *session = wpd->session;
+ enum GNUNET_DB_QueryStatus qs;
wpd->eh = NULL;
if (GNUNET_SYSERR == success)
@@ -1395,40 +1537,57 @@ wire_confirm_cb (void *cls,
wpd = NULL;
return;
}
- if (GNUNET_OK !=
- db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
- session,
- wpd->row_id))
+ qs = db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
+ session,
+ wpd->row_id);
+ if (0 >= qs)
{
- GNUNET_break (0); /* why!? */
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
db_plugin->rollback (db_plugin->cls,
session);
- global_ret = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ /* try again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ NULL);
+ }
+ else
+ {
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ }
GNUNET_free (wpd);
wpd = NULL;
return;
}
GNUNET_free (wpd);
wpd = NULL;
- if (GNUNET_OK !=
- db_plugin->commit (db_plugin->cls,
- session))
+ switch (commit_or_warn (session))
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Failed to commit database transaction!\n");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
/* try again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Wire transfer complete\n");
+ /* continue with #run_transfers(), just to guard
+ against the unlikely case that there are more. */
+ task = GNUNET_SCHEDULER_add_now (&run_transfers,
+ NULL);
+ return;
+ default:
+ GNUNET_break (0);
+ global_ret = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Wire transfer complete\n");
- /* continue with #run_transfers(), just to guard
- against the unlikely case that there are more. */
- task = GNUNET_SCHEDULER_add_now (&run_transfers,
- NULL);
-
}
@@ -1481,12 +1640,12 @@ wire_prepare_cb (void *cls,
static void
run_transfers (void *cls)
{
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
struct TALER_EXCHANGEDB_Session *session;
const struct GNUNET_SCHEDULER_TaskContext *tc;
task = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking for pending wire transfers\n");
tc = GNUNET_SCHEDULER_get_task_context ();
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
@@ -1511,35 +1670,39 @@ run_transfers (void *cls)
}
wpd = GNUNET_new (struct WirePrepareData);
wpd->session = session;
- ret = db_plugin->wire_prepare_data_get (db_plugin->cls,
- session,
- &wire_prepare_cb,
- NULL);
- if (GNUNET_SYSERR == ret)
+ qs = db_plugin->wire_prepare_data_get (db_plugin->cls,
+ session,
+ &wire_prepare_cb,
+ NULL);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ return; /* continues in #wire_prepare_cb() */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ GNUNET_free (wpd);
+ wpd = NULL;
+ switch (qs)
{
- GNUNET_break (0); /* why? how to best recover? */
- db_plugin->rollback (db_plugin->cls,
- session);
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
- GNUNET_free (wpd);
- wpd = NULL;
return;
- }
- if (GNUNET_NO == ret)
- {
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* try again */
+ task = GNUNET_SCHEDULER_add_now (&run_transfers,
+ NULL);
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* no more prepared wire transfers, go back to aggregation! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No more pending wire transfers, starting aggregation\n");
- db_plugin->rollback (db_plugin->cls,
- session);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
- GNUNET_free (wpd);
- wpd = NULL;
return;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* should be impossible */
+ GNUNET_assert (0);
}
- /* otherwise, continues in #wire_prepare_cb() */
}
diff --git a/src/exchange/taler-exchange-httpd.c
b/src/exchange/taler-exchange-httpd.c
index dbc270b..1a4d286 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -31,12 +31,16 @@
#include "taler-exchange-httpd_admin.h"
#include "taler-exchange-httpd_deposit.h"
#include "taler-exchange-httpd_refund.h"
-#include "taler-exchange-httpd_reserve.h"
+#include "taler-exchange-httpd_reserve_status.h"
+#include "taler-exchange-httpd_reserve_withdraw.h"
#include "taler-exchange-httpd_payback.h"
-#include "taler-exchange-httpd_wire.h"
-#include "taler-exchange-httpd_refresh.h"
-#include "taler-exchange-httpd_tracking.h"
+#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_track_transfer.h"
+#include "taler-exchange-httpd_track_transaction.h"
#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_wire.h"
#if HAVE_DEVELOPER
#include "taler-exchange-httpd_test.h"
#endif
diff --git a/src/exchange/taler-exchange-httpd_admin.c
b/src/exchange/taler-exchange-httpd_admin.c
index 8bb4b49..83bfedf 100644
--- a/src/exchange/taler-exchange-httpd_admin.c
+++ b/src/exchange/taler-exchange-httpd_admin.c
@@ -27,6 +27,99 @@
#include "taler-exchange-httpd_validation.h"
+/**
+ * Closure for #admin_add_incoming_transaction()
+ */
+struct AddIncomingContext
+{
+ /**
+ * public key of the reserve
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * amount to add to the reserve
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * When did we receive the wire transfer
+ */
+ struct GNUNET_TIME_Absolute execution_time;
+
+ /**
+ * which account send the funds
+ */
+ json_t *sender_account_details;
+
+ /**
+ * Information that uniquely identifies the transfer
+ */
+ json_t *transfer_details;
+
+ /**
+ * Set to the transaction status.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Add an incoming transaction to the database. Checks if the
+ * transaction is fresh (not a duplicate) and if so adds it to
+ * 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. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure with the `struct AddIncomingContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+admin_add_incoming_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct AddIncomingContext *aic = cls;
+ void *json_str;
+
+ json_str = json_dumps (aic->transfer_details,
+ JSON_INDENT(2));
+ if (NULL == json_str)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_PARSER_OUT_OF_MEMORY);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ aic->qs = TEH_plugin->reserves_in_insert (TEH_plugin->cls,
+ session,
+ &aic->reserve_pub,
+ &aic->amount,
+ aic->execution_time,
+ aic->sender_account_details,
+ json_str,
+ strlen (json_str));
+ free (json_str);
+
+ if (GNUNET_DB_STATUS_HARD_ERROR == aic->qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_ADMIN_ADD_INCOMING_DB_STORE);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return aic->qs;
+}
+
/**
* Handle a "/admin/add/incoming" request. Parses the
@@ -47,23 +140,20 @@ TEH_ADMIN_handler_admin_add_incoming (struct
TEH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size)
{
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_Amount amount;
- struct GNUNET_TIME_Absolute at;
+ struct AddIncomingContext aic;
enum TALER_ErrorCode ec;
char *emsg;
- json_t *sender_account_details;
- json_t *transfer_details;
json_t *root;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("reserve_pub", &reserve_pub),
- TALER_JSON_spec_amount ("amount", &amount),
- GNUNET_JSON_spec_absolute_time ("execution_date", &at),
- GNUNET_JSON_spec_json ("sender_account_details", &sender_account_details),
- GNUNET_JSON_spec_json ("transfer_details", &transfer_details),
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub", &aic.reserve_pub),
+ TALER_JSON_spec_amount ("amount", &aic.amount),
+ GNUNET_JSON_spec_absolute_time ("execution_date", &aic.execution_time),
+ GNUNET_JSON_spec_json ("sender_account_details",
&aic.sender_account_details),
+ GNUNET_JSON_spec_json ("transfer_details", &aic.transfer_details),
GNUNET_JSON_spec_end ()
};
int res;
+ int mhd_ret;
res = TEH_PARSE_post_json (connection,
connection_cls,
@@ -72,7 +162,8 @@ TEH_ADMIN_handler_admin_add_incoming (struct
TEH_RequestHandler *rh,
&root);
if (GNUNET_SYSERR == res)
return MHD_NO;
- if ( (GNUNET_NO == res) || (NULL == root) )
+ if ( (GNUNET_NO == res) ||
+ (NULL == root) )
return MHD_YES;
res = TEH_PARSE_json_data (connection,
root,
@@ -85,37 +176,43 @@ TEH_ADMIN_handler_admin_add_incoming (struct
TEH_RequestHandler *rh,
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
if (TALER_EC_NONE !=
- (ec = TEH_json_validate_wireformat (sender_account_details,
+ (ec = TEH_json_validate_wireformat (aic.sender_account_details,
GNUNET_NO,
&emsg)))
{
GNUNET_JSON_parse_free (spec);
- res = TEH_RESPONSE_reply_external_error (connection,
- ec,
- emsg);
+ mhd_ret = TEH_RESPONSE_reply_external_error (connection,
+ ec,
+ emsg);
GNUNET_free (emsg);
- return res;
+ return mhd_ret;
}
- if (0 != strcasecmp (amount.currency,
+ if (0 != strcasecmp (aic.amount.currency,
TEH_exchange_currency_string))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Exchange uses currency `%s', but /admin/add/incoming tried to
use currency `%s'\n",
TEH_exchange_currency_string,
- amount.currency);
+ aic.amount.currency);
GNUNET_JSON_parse_free (spec);
return TEH_RESPONSE_reply_arg_invalid (connection,
TALER_EC_ADMIN_ADD_INCOMING_CURRENCY_UNSUPPORTED,
"amount:currency");
}
- res = TEH_DB_execute_admin_add_incoming (connection,
- &reserve_pub,
- &amount,
- at,
- sender_account_details,
- transfer_details);
+ res = TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &admin_add_incoming_transaction,
+ &aic);
GNUNET_JSON_parse_free (spec);
- return res;
+ if (GNUNET_OK != res)
+ return mhd_ret;
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s}",
+ "status",
+ (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
aic.qs)
+ ? "NEW"
+ : "DUP");
}
/* end of taler-exchange-httpd_admin.c */
diff --git a/src/exchange/taler-exchange-httpd_db.c
b/src/exchange/taler-exchange-httpd_db.c
index 51e1ef5..f2e1f7b 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -15,7 +15,7 @@
*/
/**
* @file taler-exchange-httpd_db.c
- * @brief High-level (transactional-layer) database operations for the
exchange.
+ * @brief Generic database operations for the exchange.
* @author Christian Grothoff
*/
#include "platform.h"
@@ -32,84 +32,84 @@
*/
#define MAX_TRANSACTION_COMMIT_RETRIES 3
-/**
- * Code to begin a transaction, must be inline as we define a block
- * that ends with #COMMIT_TRANSACTION() within which we perform a number
- * of retries. Note that this code may call "return" internally, so
- * it must be called within a function where any cleanup will be done
- * by the caller. Furthermore, the function's return value must
- * match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define START_TRANSACTION(session,connection) \
-{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
- unsigned int transaction_retries = 0; \
- int transaction_commit_result; \
-transaction_start_label: /* we will use goto for retries */ \
- if (GNUNET_OK != \
- TEH_plugin->start (TEH_plugin->cls, \
- session)) \
- { \
- GNUNET_break (0); \
- return TEH_RESPONSE_reply_internal_db_error (connection, \
- TALER_EC_DB_START_FAILED);
\
- }
/**
- * Code to conclude a transaction, dual to #START_TRANSACTION(). Note
- * that this code may call "return" internally, so it must be called
- * within a function where any cleanup will be done by the caller.
- * Furthermore, the function's return value must match that of a
- * #TEH_RESPONSE_reply_internal_db_error() status code.
- *
- * @param session session handle
- * @param connection connection handle
+ * Run a database transaction for @a connection.
+ * Starts a transaction and calls @a cb. Upon success,
+ * attempts to commit the transaction. Upon soft failures,
+ * retries @a cb a few times. Upon hard or persistent soft
+ * errors, generates an error message for @a connection.
+ *
+ * @param connection MHD connection to run @a cb for
+ * @param[out] set to MHD response code, if transaction failed
+ * @param cb callback implementing transaction logic
+ * @param cb_cls closure for @a cb, must be read-only!
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/
-#define COMMIT_TRANSACTION(session,connection) \
- transaction_commit_result = \
- TEH_plugin->commit (TEH_plugin->cls, \
- session); \
- if (GNUNET_SYSERR == transaction_commit_result) \
- { \
- TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
- return TEH_RESPONSE_reply_commit_error (connection, \
- TALER_EC_DB_COMMIT_FAILED_HARD); \
- } \
- if (GNUNET_NO == transaction_commit_result) \
- { \
- TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
- if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
- goto transaction_start_label; \
- TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
- transaction_retries, \
- __FUNCTION__); \
- return TEH_RESPONSE_reply_commit_error (connection, \
-
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
- } \
-} /* end of scope opened by BEGIN_TRANSACTION */
+int
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+ int *mhd_ret,
+ TEH_DB_TransactionCallback cb,
+ void *cb_cls)
+{
+ struct TALER_EXCHANGEDB_Session *session;
+ if (NULL != mhd_ret)
+ *mhd_ret = -1; /* invalid value */
+ if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
+ {
+ GNUNET_break (0);
+ if (NULL != mhd_ret)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_DB_SETUP_FAILED);
+ return GNUNET_SYSERR;
+ }
+ for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES;
retries++)
+ {
+ enum GNUNET_DB_QueryStatus qs;
-/**
- * Code to include to retry a transaction, must only be used in between
- * #START_TRANSACTION and #COMMIT_TRANSACTION.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define RETRY_TRANSACTION(session,connection)
\
- do {
\
- TEH_plugin->rollback (TEH_plugin->cls,
\
- session);
\
- if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)
\
- goto transaction_start_label;
\
- TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",
\
- transaction_retries,
\
- __FUNCTION__);
\
- return TEH_RESPONSE_reply_commit_error (connection,
\
-
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
- } while (0)
+ if (GNUNET_OK !=
+ TEH_plugin->start (TEH_plugin->cls,
+ session))
+ {
+ GNUNET_break (0);
+ if (NULL != mhd_ret)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_DB_START_FAILED);
+ return GNUNET_SYSERR;
+ }
+ qs = cb (cb_cls,
+ connection,
+ session,
+ mhd_ret);
+ if (0 > qs)
+ TEH_plugin->rollback (TEH_plugin->cls,
+ session);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ return GNUNET_SYSERR;
+ if (0 <= qs)
+ qs = TEH_plugin->commit (TEH_plugin->cls,
+ session);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ if (NULL != mhd_ret)
+ *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+
TALER_EC_DB_COMMIT_FAILED_HARD);
+ return GNUNET_SYSERR;
+ }
+ /* make sure callback did not violate invariants! */
+ GNUNET_assert ( (NULL == mhd_ret) ||
+ (-1 == *mhd_ret) );
+ if (0 <= qs)
+ return GNUNET_OK;
+ }
+ TALER_LOG_WARNING ("Transaction commit failed %u times\n",
+ MAX_TRANSACTION_COMMIT_RETRIES);
+ if (NULL != mhd_ret)
+ *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
+ return GNUNET_SYSERR;
+}
/**
@@ -119,13 +119,14 @@ transaction_start_label: /* we will use goto for retries
*/ \
*
* @param tl transaction list to process
* @param off offset to use as the starting value
- * @param ret where the resulting total is to be stored
+ * @param[out] ret where the resulting total is to be stored
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
-static int
-calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
- const struct TALER_Amount *off,
- struct TALER_Amount *ret)
+// FIXME: maybe move to another module, i.e. exchangedb???
+int
+TEH_DB_calculate_transaction_list_totals (struct
TALER_EXCHANGEDB_TransactionList *tl,
+ const struct TALER_Amount *off,
+ struct TALER_Amount *ret)
{
struct TALER_Amount spent = *off;
struct TALER_EXCHANGEDB_TransactionList *pos;
@@ -206,2259 +207,4 @@ calculate_transaction_list_totals (struct
TALER_EXCHANGEDB_TransactionList *tl,
}
-/**
- * Execute a deposit. The validity of the coin and signature
- * have already been checked. The database must now check that
- * the coin is not (double or over) spent, and execute the
- * transaction (record details, generate success or failure response).
- *
- * @param connection the MHD connection to handle
- * @param deposit information about the deposit
- * @return MHD result code
- */
-int
-TEH_DB_execute_deposit (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Deposit *deposit)
-{
- struct TALER_EXCHANGEDB_Session *session;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_Amount spent;
- struct TALER_Amount value;
- struct TALER_Amount amount_without_fee;
- struct TEH_KS_StateHandle *mks;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- int ret;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- if (GNUNET_YES ==
- TEH_plugin->have_deposit (TEH_plugin->cls,
- session,
- deposit))
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_subtract (&amount_without_fee,
- &deposit->amount_with_fee,
- &deposit->deposit_fee));
- return TEH_RESPONSE_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);
- }
-
- /* FIXME: move the 'mks'-logic outside of _db.c? */
- mks = TEH_KS_acquire ();
- dki = TEH_KS_denomination_key_lookup (mks,
- &deposit->coin.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == dki)
- {
- TEH_KS_release (mks);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
- }
- TALER_amount_ntoh (&value,
- &dki->issue.properties.value);
- TEH_KS_release (mks);
-
- START_TRANSACTION (session, connection);
-
- /* fee for THIS transaction */
- spent = deposit->amount_with_fee;
- /* add cost of all previous transactions */
- tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- session,
- &deposit->coin.coin_pub);
- if (GNUNET_OK !=
- calculate_transaction_list_totals (tl,
- &spent,
- &spent))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
- }
- /* Check that cost of all transactions is smaller than
- the value of the coin. */
- if (0 < TALER_amount_cmp (&spent,
- &value))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
-
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
- tl);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return ret;
- }
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- if (GNUNET_OK !=
- TEH_plugin->insert_deposit (TEH_plugin->cls,
- session,
- deposit))
- {
- TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_DEPOSIT_STORE_DB_ERROR);
- }
-
- COMMIT_TRANSACTION(session, connection);
- GNUNET_assert (GNUNET_SYSERR !=
- TALER_amount_subtract (&amount_without_fee,
- &deposit->amount_with_fee,
- &deposit->deposit_fee));
- return TEH_RESPONSE_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);
-}
-
-
-/**
- * Execute a "/refund". Returns a confirmation that the refund
- * was successful, or a failure if we are not aware of a matching
- * /deposit or if it is too late to do the refund.
- *
- * @param connection the MHD connection to handle
- * @param refund refund details
- * @return MHD result code
- */
-int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Refund *refund)
-{
- struct TALER_EXCHANGEDB_Session *session;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_EXCHANGEDB_TransactionList *tlp;
- const struct TALER_EXCHANGEDB_Deposit *dep;
- const struct TALER_EXCHANGEDB_Refund *ref;
- struct TEH_KS_StateHandle *mks;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- struct TALER_Amount expect_fee;
- int ret;
- int deposit_found;
- int refund_found;
- int done;
- int fee_cmp;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- dep = NULL;
- ref = NULL;
- START_TRANSACTION (session, connection);
- tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- session,
- &refund->coin.coin_pub);
- if (NULL == tl)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_refund_failure (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND);
- }
- deposit_found = GNUNET_NO;
- refund_found = GNUNET_NO;
- for (tlp = tl; NULL != tlp; tlp = tlp->next)
- {
- switch (tlp->type)
- {
- case TALER_EXCHANGEDB_TT_DEPOSIT:
- if (GNUNET_NO == deposit_found)
- {
- if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
- &refund->merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) &&
- (0 == memcmp (&tlp->details.deposit->h_contract_terms,
- &refund->h_contract_terms,
- sizeof (struct GNUNET_HashCode))) )
- {
- dep = tlp->details.deposit;
- deposit_found = GNUNET_YES;
- break;
- }
- }
- break;
- case TALER_EXCHANGEDB_TT_REFRESH_MELT:
- /* Melts cannot be refunded, ignore here */
- break;
- case TALER_EXCHANGEDB_TT_REFUND:
- if (GNUNET_NO == refund_found)
- {
- /* First, check if existing refund request is identical */
- if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
- &refund->merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) &&
- (0 == memcmp (&tlp->details.refund->h_contract_terms,
- &refund->h_contract_terms,
- sizeof (struct GNUNET_HashCode))) &&
- (tlp->details.refund->rtransaction_id == refund->rtransaction_id)
)
- {
- ref = tlp->details.refund;
- refund_found = GNUNET_YES;
- break;
- }
- /* Second, check if existing refund request conflicts */
- if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
- &refund->merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) &&
- (0 == memcmp (&tlp->details.refund->h_contract_terms,
- &refund->h_contract_terms,
- sizeof (struct GNUNET_HashCode))) &&
- (tlp->details.refund->rtransaction_id != refund->rtransaction_id)
)
- {
- GNUNET_break_op (0); /* conflicting refund found */
- refund_found = GNUNET_SYSERR;
- /* NOTE: Alternatively we could total up all existing
- refunds and check if the sum still permits the
- refund requested (thus allowing multiple, partial
- refunds). Fow now, we keep it simple. */
- break;
- }
- }
- break;
- case TALER_EXCHANGEDB_TT_PAYBACK:
- /* Paybacks cannot be refunded, ignore here */
- break;
- }
- }
- /* handle if deposit was NOT found */
- if (GNUNET_NO == deposit_found)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_transaction_unknown (connection,
-
TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
- }
- /* handle if conflicting refund found */
- if (GNUNET_SYSERR == refund_found)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- ret = TEH_RESPONSE_reply_refund_conflict (connection,
- tl);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return ret;
- }
- /* handle if identical refund found */
- if (GNUNET_YES == refund_found)
- {
- /* /refund already done, simply re-transmit confirmation */
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- ret = TEH_RESPONSE_reply_refund_success (connection,
- ref);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return ret;
- }
-
- /* check currency is compatible */
- if ( (GNUNET_YES !=
- TALER_amount_cmp_currency (&refund->refund_amount,
- &dep->amount_with_fee)) ||
- (GNUNET_YES !=
- TALER_amount_cmp_currency (&refund->refund_fee,
- &dep->deposit_fee)) )
- {
- GNUNET_break_op (0); /* currency missmatch */
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_refund_failure (connection,
- MHD_HTTP_PRECONDITION_FAILED,
-
TALER_EC_REFUND_CURRENCY_MISSMATCH);
- }
-
- /* check if we already send the money for the /deposit */
- done = TEH_plugin->test_deposit_done (TEH_plugin->cls,
- session,
- dep);
- if (GNUNET_SYSERR == done)
- {
- /* Internal error, we first had the deposit in the history,
- but now it is gone? */
- GNUNET_break (0);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_error (connection,
- TALER_EC_REFUND_DB_INCONSISTENT,
- "database inconsistent");
- }
- if (GNUNET_YES == done)
- {
- /* money was already transferred to merchant, can no longer refund */
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_refund_failure (connection,
- MHD_HTTP_GONE,
-
TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
- }
-
- /* check refund amount is sufficiently low */
- if (1 == TALER_amount_cmp (&refund->refund_amount,
- &dep->amount_with_fee) )
- {
- GNUNET_break_op (0); /* cannot refund more than original value */
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_refund_failure (connection,
- MHD_HTTP_PRECONDITION_FAILED,
-
TALER_EC_REFUND_INSUFFICIENT_FUNDS);
- }
-
- /* Check refund fee matches fee of denomination key! */
- mks = TEH_KS_acquire ();
- dki = TEH_KS_denomination_key_lookup (mks,
- &dep->coin.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == dki)
- {
- /* DKI not found, but we do have a coin with this DK in our database;
- not good... */
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_KS_release (mks);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
- "denomination key not found");
- }
- TALER_amount_ntoh (&expect_fee,
- &dki->issue.properties.fee_refund);
- fee_cmp = TALER_amount_cmp (&refund->refund_fee,
- &expect_fee);
- TEH_KS_release (mks);
-
- if (-1 == fee_cmp)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_arg_invalid (connection,
- TALER_EC_REFUND_FEE_TOO_LOW,
- "refund_fee");
- }
- if (1 == fee_cmp)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Refund fee proposed by merchant is higher than necessary.\n");
- }
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
-
- /* Finally, store new refund data */
- if (GNUNET_OK !=
- TEH_plugin->insert_refund (TEH_plugin->cls,
- session,
- refund))
- {
- TALER_LOG_WARNING ("Failed to store /refund information in database\n");
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFUND_STORE_DB_ERROR);
- }
- COMMIT_TRANSACTION (session, connection);
-
- return TEH_RESPONSE_reply_refund_success (connection,
- refund);
-}
-
-
-/**
- * Execute a /reserve/status. Given the public key of a reserve,
- * return the associated transaction history.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve to check
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_status (struct MHD_Connection *connection,
- const struct TALER_ReservePublicKeyP
*reserve_pub)
-{
- struct TALER_EXCHANGEDB_Session *session;
- struct TALER_EXCHANGEDB_ReserveHistory *rh;
- int res;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- START_TRANSACTION (session, connection);
- rh = TEH_plugin->get_reserve_history (TEH_plugin->cls,
- session,
- reserve_pub);
- COMMIT_TRANSACTION (session, connection);
- if (NULL == rh)
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_NOT_FOUND,
- "{s:s, s:s}",
- "error", "Reserve not found",
- "parameter", "withdraw_pub");
- res = TEH_RESPONSE_reply_reserve_status_success (connection,
- rh);
- TEH_plugin->free_reserve_history (TEH_plugin->cls,
- rh);
- return res;
-}
-
-
-/**
- * Try to execute /reserve/withdraw transaction.
- *
- * @param connection request we are handling
- * @param session database session we are using
- * @param key_state key state to lookup denomination pubs
- * @param reserve reserve to withdraw from
- * @param denomination_pub public key of the denomination requested
- * @param dki denomination to withdraw
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param h_blind hash of @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @param[out] denom_sig where to write the resulting signature
- * (used to release memory in case of transaction failure
- * @return MHD result code
- */
-static int
-execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- struct TEH_KS_StateHandle *key_state,
- const struct TALER_ReservePublicKeyP
*reserve,
- const struct TALER_DenominationPublicKey
*denomination_pub,
- const struct
TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
- const char *blinded_msg,
- size_t blinded_msg_len,
- const struct GNUNET_HashCode *h_blind,
- const struct TALER_ReserveSignatureP
*signature,
- struct TALER_DenominationSignature
*denom_sig)
-{
- struct TALER_EXCHANGEDB_ReserveHistory *rh;
- const struct TALER_EXCHANGEDB_ReserveHistory *pos;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *tdki;
- struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
- struct TALER_Amount amount_required;
- struct TALER_Amount deposit_total;
- struct TALER_Amount withdraw_total;
- struct TALER_Amount balance;
- struct TALER_Amount value;
- struct TALER_Amount fee_withdraw;
- int res;
- int ret;
-
- /* Check if balance is sufficient */
- START_TRANSACTION (session, connection);
- rh = TEH_plugin->get_reserve_history (TEH_plugin->cls,
- session,
- reserve);
- if (NULL == rh)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_arg_unknown (connection,
- TALER_EC_WITHDRAW_RESERVE_UNKNOWN,
- "reserve_pub");
- }
-
- /* calculate amount required including fees */
- TALER_amount_ntoh (&value,
- &dki->issue.properties.value);
- TALER_amount_ntoh (&fee_withdraw,
- &dki->issue.properties.fee_withdraw);
-
- if (GNUNET_OK !=
- TALER_amount_add (&amount_required,
- &value,
- &fee_withdraw))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW);
- }
-
- /* calculate balance of the reserve */
- res = 0;
- for (pos = rh; NULL != pos; pos = pos->next)
- {
- switch (pos->type)
- {
- case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
- if (0 == (res & 1))
- deposit_total = pos->details.bank->amount;
- else
- if (GNUNET_OK !=
- TALER_amount_add (&deposit_total,
- &deposit_total,
- &pos->details.bank->amount))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
- }
- res |= 1;
- break;
- case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
- tdki = TEH_KS_denomination_key_lookup (key_state,
- &pos->details.withdraw->denom_pub,
- TEH_KS_DKU_WITHDRAW);
- if (NULL == tdki)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND);
- }
- TALER_amount_ntoh (&value,
- &tdki->issue.properties.value);
- if (0 == (res & 2))
- withdraw_total = value;
- else
- if (GNUNET_OK !=
- TALER_amount_add (&withdraw_total,
- &withdraw_total,
- &value))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
- }
- res |= 2;
- break;
-
- case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
- if (0 == (res & 1))
- deposit_total = pos->details.payback->value;
- else
- if (GNUNET_OK !=
- TALER_amount_add (&deposit_total,
- &deposit_total,
- &pos->details.payback->value))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
- }
- res |= 1;
- break;
-
- case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
- if (0 == (res & 2))
- withdraw_total = pos->details.bank->amount;
- else
- if (GNUNET_OK !=
- TALER_amount_add (&withdraw_total,
- &withdraw_total,
- &pos->details.bank->amount))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
- }
- res |= 2;
- break;
- }
- }
- if (0 == (res & 1))
- {
- /* did not encounter any wire transfer operations, how can we have a
reserve? */
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
- }
- if (0 == (res & 2))
- {
- /* did not encounter any withdraw operations, set to zero */
- TALER_amount_get_zero (deposit_total.currency,
- &withdraw_total);
- }
- /* All reserve balances should be non-negative */
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&balance,
- &deposit_total,
- &withdraw_total))
- {
- GNUNET_break (0); /* database inconsistent */
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
- }
- if (0 < TALER_amount_cmp (&amount_required,
- &balance))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- res = TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection,
- rh);
- TEH_plugin->free_reserve_history (TEH_plugin->cls,
- rh);
- return res;
- }
- TEH_plugin->free_reserve_history (TEH_plugin->cls,
- rh);
-
- /* Balance is good, sign the coin! */
- denom_sig->rsa_signature
- = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
- blinded_msg,
- blinded_msg_len);
- if (NULL == denom_sig->rsa_signature)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
- "Internal error");
- }
- collectable.sig = *denom_sig;
- collectable.denom_pub = *denomination_pub;
- collectable.amount_with_fee = amount_required;
- collectable.withdraw_fee = fee_withdraw;
- collectable.reserve_pub = *reserve;
- collectable.h_coin_envelope = *h_blind;
- collectable.reserve_sig = *signature;
- ret = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
- session,
- &collectable);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_DB_STORE_ERROR);
- }
- if (GNUNET_NO == ret)
- RETRY_TRANSACTION(session, connection);
- COMMIT_TRANSACTION (session, connection);
-
- return TEH_RESPONSE_reply_reserve_withdraw_success (connection,
- &collectable);
-}
-
-
-
-/**
- * Execute a "/reserve/withdraw". Given a reserve and a properly signed
- * request to withdraw a coin, check the balance of the reserve and
- * if it is sufficient, store the request and return the signed
- * blinded envelope.
- *
- * @param connection the MHD connection to handle
- * @param reserve public key of the reserve
- * @param denomination_pub public key of the denomination requested
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
- const struct TALER_ReservePublicKeyP *reserve,
- const struct TALER_DenominationPublicKey
*denomination_pub,
- const char *blinded_msg,
- size_t blinded_msg_len,
- const struct TALER_ReserveSignatureP
*signature)
-{
- struct TALER_EXCHANGEDB_Session *session;
- struct TEH_KS_StateHandle *key_state;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
- struct TALER_DenominationSignature denom_sig;
- struct GNUNET_HashCode h_blind;
- int res;
-
- GNUNET_CRYPTO_hash (blinded_msg,
- blinded_msg_len,
- &h_blind);
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- res = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
- session,
- &h_blind,
- &collectable);
- if (GNUNET_SYSERR == res)
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
- }
-
- /* Don't sign again if we have already signed the coin */
- if (GNUNET_YES == res)
- {
- res = TEH_RESPONSE_reply_reserve_withdraw_success (connection,
- &collectable);
- GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key);
- return res;
- }
- GNUNET_assert (GNUNET_NO == res);
-
- /* FIXME: do we have to do this a second time here? */
- key_state = TEH_KS_acquire ();
- dki = TEH_KS_denomination_key_lookup (key_state,
- denomination_pub,
- TEH_KS_DKU_WITHDRAW);
- if (NULL == dki)
- {
- TEH_KS_release (key_state);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_NOT_FOUND,
- "{s:s, s:I}",
- "error",
- "Denomination not found",
- "code",
- (json_int_t)
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND);
- }
- denom_sig.rsa_signature = NULL;
- res = execute_reserve_withdraw_transaction (connection,
- session,
- key_state,
- reserve,
- denomination_pub,
- dki,
- blinded_msg,
- blinded_msg_len,
- &h_blind,
- signature,
- &denom_sig);
- if (NULL != denom_sig.rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
- TEH_KS_release (key_state);
- return res;
-}
-
-
-/**
- * Parse coin melt requests from a JSON object and write them to
- * the database.
- *
- * @param connection the connection to send errors to
- * @param session the database connection
- * @param key_state the exchange's key state
- * @param session_hash hash identifying the refresh session
- * @param coin_details details about the coin being melted
- * @param[out] meltp on success, set to melt details
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if an error message was generated,
- * #GNUNET_SYSERR on internal errors (no response generated)
- */
-static int
-refresh_check_melt (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- const struct TEH_KS_StateHandle *key_state,
- const struct GNUNET_HashCode *session_hash,
- const struct TEH_DB_MeltDetails *coin_details,
- struct TALER_EXCHANGEDB_RefreshMelt *meltp)
-{
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
- struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_Amount coin_value;
- struct TALER_Amount coin_residual;
- struct TALER_Amount spent;
- int res;
-
- dk = TEH_KS_denomination_key_lookup (key_state,
- &coin_details->coin_info.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == dk)
- return (MHD_YES ==
- TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
- "denomination key no longer
available while executing transaction"))
- ? GNUNET_NO : GNUNET_SYSERR;
- dki = &dk->issue;
- TALER_amount_ntoh (&coin_value,
- &dki->properties.value);
- /* fee for THIS transaction; the melt amount includes the fee! */
- spent = coin_details->melt_amount_with_fee;
- /* add historic transaction costs of this coin */
- tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- session,
- &coin_details->coin_info.coin_pub);
- if (GNUNET_OK !=
- calculate_transaction_list_totals (tl,
- &spent,
- &spent))
- {
- GNUNET_break (0);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return (MHD_YES ==
- TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- /* Refuse to refresh when the coin's value is insufficient
- for the cost of all transactions. */
- if (TALER_amount_cmp (&coin_value,
- &spent) < 0)
- {
- GNUNET_assert (GNUNET_SYSERR !=
- TALER_amount_subtract (&coin_residual,
- &spent,
-
&coin_details->melt_amount_with_fee));
- res = (MHD_YES ==
- TEH_RESPONSE_reply_refresh_melt_insufficient_funds (connection,
-
&coin_details->coin_info.coin_pub,
- coin_value,
- tl,
-
coin_details->melt_amount_with_fee,
- coin_residual))
- ? GNUNET_NO : GNUNET_SYSERR;
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return res;
- }
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
-
- meltp->coin = coin_details->coin_info;
- meltp->coin_sig = coin_details->melt_sig;
- meltp->session_hash = *session_hash;
- meltp->amount_with_fee = coin_details->melt_amount_with_fee;
- meltp->melt_fee = coin_details->melt_fee;
- return GNUNET_OK;
-}
-
-
-/**
- * Execute a "/refresh/melt". We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub. Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of
y-dimension of @a commit_coin array
- * @param denom_pubs public keys of the coins we want to withdraw in the end
- * @param coin_melt_detail signature and (residual) value of the respective
coin should be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to
sign
- * once the "/refres/reveal" of cut and choose is done),
- * x-dimension must be #TALER_CNC_KAPPA
- * @param transfer_pubs array of transfer public keys (what the exchange is
- * to return via "/refresh/link" to enable linkage in the
- * future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- unsigned int num_new_denoms,
- const struct TALER_DenominationPublicKey
*denom_pubs,
- const struct TEH_DB_MeltDetails *coin_melt_detail,
- struct TALER_EXCHANGEDB_RefreshCommitCoin *const*
commit_coin,
- const struct TALER_TransferPublicKeyP
*transfer_pubs)
-{
- struct TEH_KS_StateHandle *key_state;
- struct TALER_EXCHANGEDB_RefreshSession refresh_session;
- struct TALER_EXCHANGEDB_Session *session;
- int res;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- START_TRANSACTION (session, connection);
- res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
- session,
- session_hash,
- &refresh_session);
- if (GNUNET_YES == res)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- res = TEH_RESPONSE_reply_refresh_melt_success (connection,
- session_hash,
-
refresh_session.noreveal_index);
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- }
- if (GNUNET_SYSERR == res)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
- }
-
- /* store 'global' session data */
- refresh_session.num_newcoins = num_new_denoms;
- refresh_session.noreveal_index
- = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
- TALER_CNC_KAPPA);
- key_state = TEH_KS_acquire ();
- if (GNUNET_OK !=
- (res = refresh_check_melt (connection,
- session,
- key_state,
- session_hash,
- coin_melt_detail,
- &refresh_session.melt)))
- {
- TEH_KS_release (key_state);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- }
- TEH_KS_release (key_state);
-
- if (GNUNET_OK !=
- (res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
- session,
- session_hash,
- &refresh_session)))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
- }
-
- /* store requested new denominations */
- if (GNUNET_OK !=
- TEH_plugin->insert_refresh_order (TEH_plugin->cls,
- session,
- session_hash,
- num_new_denoms,
- denom_pubs))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
- }
-
- if (GNUNET_OK !=
- TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
- session,
- session_hash,
- num_new_denoms,
-
commit_coin[refresh_session.noreveal_index]))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
- }
- if (GNUNET_OK !=
- TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
- session,
- session_hash,
-
&transfer_pubs[refresh_session.noreveal_index]))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
- }
-
- COMMIT_TRANSACTION (session, connection);
- return TEH_RESPONSE_reply_refresh_melt_success (connection,
- session_hash,
-
refresh_session.noreveal_index);
-}
-
-
-/**
- * Check if the given @a transfer_privs correspond to an honest
- * commitment for the given session.
- * Checks that the transfer private keys match their commitments.
- * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that
they match.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param off commitment offset to check
- * @param transfer_priv private transfer key
- * @param melt information about the melted coin
- * @param num_newcoins number of newcoins being generated
- * @param denom_pubs array of @a num_newcoins keys for the new coins
- * @param hash_context hash context to update by hashing in the data
- * from this offset
- * @return #GNUNET_OK if the committment was honest,
- * #GNUNET_NO if there was a problem and we generated an error message
- * #GNUNET_SYSERR if we could not even generate an error message
- */
-static int
-check_commitment (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- unsigned int off,
- const struct TALER_TransferPrivateKeyP *transfer_priv,
- const struct TALER_EXCHANGEDB_RefreshMelt *melt,
- unsigned int num_newcoins,
- const struct TALER_DenominationPublicKey *denom_pubs,
- struct GNUNET_HashContext *hash_context)
-{
- struct TALER_TransferSecretP transfer_secret;
- unsigned int j;
-
- TALER_link_reveal_transfer_secret (transfer_priv,
- &melt->coin.coin_pub,
- &transfer_secret);
-
- /* Check that the commitments for all new coins were correct */
- for (j = 0; j < num_newcoins; j++)
- {
- struct TALER_FreshCoinP fc;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct GNUNET_HashCode h_msg;
- char *buf;
- size_t buf_len;
-
- TALER_setup_fresh_coin (&transfer_secret,
- j,
- &fc);
- GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
- &coin_pub.eddsa_pub);
- GNUNET_CRYPTO_hash (&coin_pub,
- sizeof (struct TALER_CoinSpendPublicKeyP),
- &h_msg);
- if (GNUNET_YES !=
- GNUNET_CRYPTO_rsa_blind (&h_msg,
- &fc.blinding_key.bks,
- denom_pubs[j].rsa_public_key,
- &buf,
- &buf_len))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Blind failed (bad denomination key!?)\n");
- return (MHD_YES ==
- TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
- "Blinding error"))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- GNUNET_CRYPTO_hash_context_read (hash_context,
- buf,
- buf_len);
- GNUNET_free (buf);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Exchange a coin as part of a refresh operation. Obtains the
- * envelope from the database and performs the signing operation.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param key_state key state to lookup denomination pubs
- * @param denom_pub denomination key for the coin to create
- * @param commit_coin the coin that was committed
- * @param coin_off number of the coin
- * @return NULL on error, otherwise signature over the coin
- */
-static struct TALER_DenominationSignature
-refresh_exchange_coin (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- struct TEH_KS_StateHandle *key_state,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_EXCHANGEDB_RefreshCommitCoin
*commit_coin,
- unsigned int coin_off)
-{
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- struct TALER_DenominationSignature ev_sig;
-
- dki = TEH_KS_denomination_key_lookup (key_state,
- denom_pub,
- TEH_KS_DKU_WITHDRAW);
- if (NULL == dki)
- {
- GNUNET_break (0);
- ev_sig.rsa_signature = NULL;
- return ev_sig;
- }
- if (GNUNET_OK ==
- TEH_plugin->get_refresh_out (TEH_plugin->cls,
- session,
- session_hash,
- coin_off,
- &ev_sig))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning cashed reply for /refresh/reveal signature\n");
- return ev_sig;
- }
-
- ev_sig.rsa_signature
- = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
- commit_coin->coin_ev,
- commit_coin->coin_ev_size);
- if (NULL == ev_sig.rsa_signature)
- {
- GNUNET_break (0);
- return ev_sig;
- }
- if (GNUNET_SYSERR ==
- TEH_plugin->insert_refresh_out (TEH_plugin->cls,
- session,
- session_hash,
- coin_off,
- &ev_sig))
- {
- GNUNET_break (0);
- GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
- ev_sig.rsa_signature = NULL;
- }
-
- return ev_sig;
-}
-
-
-/**
- * The client request was well-formed, now execute the DB transaction
- * of a "/refresh/reveal" operation. We use the @a ev_sigs and
- * @a commit_coins to clean up resources after this function returns
- * as we might experience retries of the database transaction.
- *
- * @param connection the MHD connection to handle
- * @param session database session
- * @param session_hash hash identifying the refresh session
- * @param refresh_session information about the refresh operation we are doing
- * @param denom_pubs array of "num_newcoins" denomination keys for the new
coins
- * @param[out] ev_sigs where to store generated signatures for the new coins,
- * array of length "num_newcoins", memory released by the
- * caller
- * @param[out] commit_coins array of length "num_newcoins" to be used for
- * information about the new coins from the commitment.
- * @return MHD result code
- */
-static int
-execute_refresh_reveal_transaction (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- const struct
TALER_EXCHANGEDB_RefreshSession *refresh_session,
- const struct TALER_DenominationPublicKey
*denom_pubs,
- struct TALER_DenominationSignature
*ev_sigs,
- struct TALER_EXCHANGEDB_RefreshCommitCoin
*commit_coins)
-{
- unsigned int j;
- struct TEH_KS_StateHandle *key_state;
- int ret;
-
- START_TRANSACTION (session, connection);
- key_state = TEH_KS_acquire ();
- for (j=0;j<refresh_session->num_newcoins;j++)
- {
- if (NULL == ev_sigs[j].rsa_signature) /* could be non-NULL during retries
*/
- ev_sigs[j] = refresh_exchange_coin (connection,
- session,
- session_hash,
- key_state,
- &denom_pubs[j],
- &commit_coins[j],
- j);
- if (NULL == ev_sigs[j].rsa_signature)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- ret = TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
- goto cleanup;
- }
- }
- COMMIT_TRANSACTION (session, connection);
- ret = TEH_RESPONSE_reply_refresh_reveal_success (connection,
-
refresh_session->num_newcoins,
- ev_sigs);
- cleanup:
- TEH_KS_release (key_state);
- return ret;
-}
-
-
-/**
- * Execute a "/refresh/reveal". The client is revealing to us the
- * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the
- * revealed transfer keys would allow linkage to the blinded coins,
- * and if so, return the signed coins for corresponding to the set of
- * coins that was not chosen.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash identifying the refresh session
- * @param transfer_privs array with the revealed transfer keys,
- * length must be #TALER_CNC_KAPPA - 1
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- struct TALER_TransferPrivateKeyP
*transfer_privs)
-{
- int res;
- struct TALER_EXCHANGEDB_Session *session;
- struct TALER_EXCHANGEDB_RefreshSession refresh_session;
- struct TALER_DenominationPublicKey *denom_pubs;
- struct TALER_DenominationSignature *ev_sigs;
- struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
- unsigned int i;
- unsigned int j;
- unsigned int off;
- struct GNUNET_HashContext *hash_context;
- struct GNUNET_HashCode sh_check;
- int ret;
- struct TALER_TransferPublicKeyP gamma_tp;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
-
- res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
- session,
- session_hash,
- &refresh_session);
- if (GNUNET_NO == res)
- return TEH_RESPONSE_reply_arg_invalid (connection,
-
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
- "session_hash");
- if ( (GNUNET_SYSERR == res) ||
- (refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
- denom_pubs = GNUNET_new_array (refresh_session.num_newcoins,
- struct TALER_DenominationPublicKey);
- if (GNUNET_OK !=
- TEH_plugin->get_refresh_order (TEH_plugin->cls,
- session,
- session_hash,
- refresh_session.num_newcoins,
- denom_pubs))
- {
- GNUNET_break (0);
- GNUNET_free (denom_pubs);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
- return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
-
- hash_context = GNUNET_CRYPTO_hash_context_start ();
- /* first, iterate over transfer public keys for hash_context */
- off = 0;
- for (i=0;i<TALER_CNC_KAPPA;i++)
- {
- if (i == refresh_session.noreveal_index)
- {
- off = 1;
- /* obtain gamma_tp from db */
- if (GNUNET_OK !=
- TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
- session,
- session_hash,
- &gamma_tp))
- {
- GNUNET_break (0);
- GNUNET_free (denom_pubs);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &gamma_tp,
- sizeof (struct
TALER_TransferPublicKeyP));
- }
- else
- {
- /* compute tp from private key */
- struct TALER_TransferPublicKeyP tp;
-
- GNUNET_CRYPTO_ecdhe_key_get_public (&transfer_privs[i - off].ecdhe_priv,
- &tp.ecdhe_pub);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &tp,
- sizeof (struct
TALER_TransferPublicKeyP));
- }
- }
-
- /* next, add all of the hashes from the denomination keys to the
- hash_context */
- {
- struct TALER_DenominationPublicKey
denom_pubs[refresh_session.num_newcoins];
-
- if (GNUNET_OK !=
- TEH_plugin->get_refresh_order (TEH_plugin->cls,
- session,
- session_hash,
- refresh_session.num_newcoins,
- denom_pubs))
- {
- GNUNET_break (0);
- GNUNET_free (denom_pubs);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- for (i=0;i<refresh_session.num_newcoins;i++)
- {
- char *buf;
- size_t buf_size;
-
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode
(denom_pubs[i].rsa_public_key,
- &buf);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- buf,
- buf_size);
- GNUNET_free (buf);
- GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key);
- }
- }
-
- /* next, add public key of coin and amount being refreshed */
- {
- struct TALER_AmountNBO melt_amountn;
-
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &refresh_session.melt.coin.coin_pub,
- sizeof (struct
TALER_CoinSpendPublicKeyP));
- TALER_amount_hton (&melt_amountn,
- &refresh_session.melt.amount_with_fee);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &melt_amountn,
- sizeof (struct TALER_AmountNBO));
- }
-
- commit_coins = GNUNET_new_array (refresh_session.num_newcoins,
- struct TALER_EXCHANGEDB_RefreshCommitCoin);
- off = 0;
- for (i=0;i<TALER_CNC_KAPPA;i++)
- {
- if (i == refresh_session.noreveal_index)
- {
- off = 1;
- /* obtain commit_coins for the selected gamma value from DB */
- if (GNUNET_OK !=
- TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
- session,
- session_hash,
- refresh_session.num_newcoins,
- commit_coins))
- {
- GNUNET_break (0);
- GNUNET_free (denom_pubs);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
- }
- /* add envelopes to hash_context */
- for (j=0;j<refresh_session.num_newcoins;j++)
- {
- GNUNET_CRYPTO_hash_context_read (hash_context,
- commit_coins[j].coin_ev,
- commit_coins[j].coin_ev_size);
- }
- continue;
- }
- if (GNUNET_OK !=
- (res = check_commitment (connection,
- session,
- session_hash,
- i,
- &transfer_privs[i - off],
- &refresh_session.melt,
- refresh_session.num_newcoins,
- denom_pubs,
- hash_context)))
- {
- GNUNET_break_op (0);
- for (j=0;j<refresh_session.num_newcoins;j++)
- {
- GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
- GNUNET_free (commit_coins[j].coin_ev);
- }
- GNUNET_free (commit_coins);
- GNUNET_free (denom_pubs);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
- }
-
- /* Check session hash matches */
- GNUNET_CRYPTO_hash_context_finish (hash_context,
- &sh_check);
- if (0 != memcmp (&sh_check,
- session_hash,
- sizeof (struct GNUNET_HashCode)))
- {
- GNUNET_break_op (0);
- ret = TEH_RESPONSE_reply_refresh_reveal_missmatch (connection,
- &refresh_session,
- commit_coins,
- denom_pubs,
- &gamma_tp);
- for (j=0;j<refresh_session.num_newcoins;j++)
- {
- GNUNET_free (commit_coins[j].coin_ev);
- GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
- }
- GNUNET_free (commit_coins);
- GNUNET_free (denom_pubs);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-
- return ret;
- }
-
- /* Client request OK, start transaction */
- ev_sigs = GNUNET_new_array (refresh_session.num_newcoins,
- struct TALER_DenominationSignature);
-
- /* FIXME: might need to store revealed transfer private keys for
- the auditor for later; should pass them as arguments here! #4792*/
- res = execute_refresh_reveal_transaction (connection,
- session,
- session_hash,
- &refresh_session,
- denom_pubs,
- ev_sigs,
- commit_coins);
- for (i=0;i<refresh_session.num_newcoins;i++)
- {
- if (NULL != ev_sigs[i].rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
- GNUNET_free (commit_coins[i].coin_ev);
- }
- for (j=0;j<refresh_session.num_newcoins;j++)
- if (NULL != denom_pubs[j].rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
- GNUNET_CRYPTO_rsa_signature_free
(refresh_session.melt.coin.denom_sig.rsa_signature);
- GNUNET_CRYPTO_rsa_public_key_free
(refresh_session.melt.coin.denom_pub.rsa_public_key);
- GNUNET_free (ev_sigs);
- GNUNET_free (denom_pubs);
- GNUNET_free (commit_coins);
- return res;
-}
-
-
-/**
- * Closure for #handle_transfer_data().
- */
-struct HTD_Context
-{
-
- /**
- * Session link data we collect.
- */
- struct TEH_RESPONSE_LinkSessionInfo *sessions;
-
- /**
- * Database session. Nothing to do with @a sessions.
- */
- struct TALER_EXCHANGEDB_Session *session;
-
- /**
- * MHD connection, for queueing replies.
- */
- struct MHD_Connection *connection;
-
- /**
- * Number of sessions the coin was melted into.
- */
- unsigned int num_sessions;
-
- /**
- * How are we expected to proceed. #GNUNET_SYSERR if we
- * failed to return an error (should return #MHD_NO).
- * #GNUNET_NO if we succeeded in queueing an MHD error
- * (should return #MHD_YES from #TEH_execute_refresh_link),
- * #GNUNET_OK if we should call #TEH_RESPONSE_reply_refresh_link_success().
- */
- int status;
-};
-
-
-/**
- * Function called with the session hashes and transfer secret
- * information for a given coin. Gets the linkage data and
- * builds the reply for the client.
- *
- *
- * @param cls closure, a `struct HTD_Context`
- * @param session_hash a session the coin was melted in
- * @param transfer_pub public transfer key for the session
- */
-static void
-handle_transfer_data (void *cls,
- const struct GNUNET_HashCode *session_hash,
- const struct TALER_TransferPublicKeyP *transfer_pub)
-{
- struct HTD_Context *ctx = cls;
- struct TALER_EXCHANGEDB_LinkDataList *ldl;
- struct TEH_RESPONSE_LinkSessionInfo *lsi;
-
- if (GNUNET_OK != ctx->status)
- return;
- ldl = TEH_plugin->get_link_data_list (TEH_plugin->cls,
- ctx->session,
- session_hash);
- if (NULL == ldl)
- {
- ctx->status = GNUNET_NO;
- if (MHD_NO ==
- TEH_RESPONSE_reply_json_pack (ctx->connection,
- MHD_HTTP_NOT_FOUND,
- "{s:s}",
- "error",
- "link data not found (link)"))
- ctx->status = GNUNET_SYSERR;
- return;
- }
- GNUNET_array_grow (ctx->sessions,
- ctx->num_sessions,
- ctx->num_sessions + 1);
- lsi = &ctx->sessions[ctx->num_sessions - 1];
- lsi->transfer_pub = *transfer_pub;
- lsi->ldl = ldl;
-}
-
-
-/**
- * Execute a "/refresh/link". Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to
- * the refreshed coin.
- *
- * @param connection the MHD connection to handle
- * @param coin_pub public key of the coin to link
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP *coin_pub)
-{
- struct HTD_Context ctx;
- int res;
- unsigned int i;
-
- if (NULL == (ctx.session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- ctx.connection = connection;
- ctx.num_sessions = 0;
- ctx.sessions = NULL;
- ctx.status = GNUNET_OK;
- res = TEH_plugin->get_transfer (TEH_plugin->cls,
- ctx.session,
- coin_pub,
- &handle_transfer_data,
- &ctx);
- if (GNUNET_SYSERR == ctx.status)
- {
- res = MHD_NO;
- goto cleanup;
- }
- if (GNUNET_NO == ctx.status)
- {
- res = MHD_YES;
- goto cleanup;
- }
- GNUNET_assert (GNUNET_OK == ctx.status);
- if (0 == ctx.num_sessions)
- return TEH_RESPONSE_reply_arg_unknown (connection,
- TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
- "coin_pub");
- res = TEH_RESPONSE_reply_refresh_link_success (connection,
- ctx.num_sessions,
- ctx.sessions);
- cleanup:
- for (i=0;i<ctx.num_sessions;i++)
- TEH_plugin->free_link_data_list (TEH_plugin->cls,
- ctx.sessions[i].ldl);
- GNUNET_free_non_null (ctx.sessions);
- return res;
-}
-
-
-/**
- * Add an incoming transaction to the database. Checks if the
- * transaction is fresh (not a duplicate) and if so adds it to
- * the database.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve
- * @param amount amount to add to the reserve
- * @param execution_time when did we receive the wire transfer
- * @param sender_account_details which account send the funds
- * @param transfer_details information that uniquely identifies the transfer
- * @return MHD result code
- */
-int
-TEH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
- const struct TALER_ReservePublicKeyP
*reserve_pub,
- const struct TALER_Amount *amount,
- struct GNUNET_TIME_Absolute execution_time,
- const json_t *sender_account_details,
- const json_t *transfer_details)
-{
- struct TALER_EXCHANGEDB_Session *session;
- int ret;
- void *json_str;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- json_str = json_dumps (transfer_details,
- JSON_INDENT(2));
- if (NULL == json_str)
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_PARSER_OUT_OF_MEMORY);
- }
- ret = TEH_plugin->reserves_in_insert (TEH_plugin->cls,
- session,
- reserve_pub,
- amount,
- execution_time,
- sender_account_details,
- json_str,
- strlen (json_str));
- free (json_str);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_ADMIN_ADD_INCOMING_DB_STORE);
- }
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:s}",
- "status",
- (GNUNET_OK == ret)
- ? "NEW"
- : "DUP");
-}
-
-
-/**
- * Closure for #handle_transaction_data.
- */
-struct WtidTransactionContext
-{
-
- /**
- * Total amount of the wire transfer, as calculated by
- * summing up the individual amounts. To be rounded down
- * to calculate the real transfer amount at the end.
- * Only valid if @e is_valid is #GNUNET_YES.
- */
- struct TALER_Amount total;
-
- /**
- * Public key of the merchant, only valid if @e is_valid
- * is #GNUNET_YES.
- */
- struct TALER_MerchantPublicKeyP merchant_pub;
-
- /**
- * Which method was used to wire the funds?
- */
- char *wire_method;
-
- /**
- * Hash of the wire details of the merchant (identical for all
- * deposits), only valid if @e is_valid is #GNUNET_YES.
- */
- struct GNUNET_HashCode h_wire;
-
- /**
- * Execution time of the wire transfer
- */
- struct GNUNET_TIME_Absolute exec_time;
-
- /**
- * Head of DLL with details for /wire/deposit response.
- */
- struct TEH_TrackTransferDetail *wdd_head;
-
- /**
- * Head of DLL with details for /wire/deposit response.
- */
- struct TEH_TrackTransferDetail *wdd_tail;
-
- /**
- * JSON array with details about the individual deposits.
- */
- json_t *deposits;
-
- /**
- * Initially #GNUNET_NO, if we found no deposits so far. Set to
- * #GNUNET_YES if we got transaction data, and the database replies
- * remained consistent with respect to @e merchant_pub and @e h_wire
- * (as they should). Set to #GNUNET_SYSERR if we encountered an
- * internal error.
- */
- int is_valid;
-
-};
-
-
-/**
- * Function called with the results of the lookup of the
- * transaction data for the given wire transfer identifier.
- *
- * @param cls our context for transmission
- * @param rowid which row in the DB is the information from (for diagnostics)
- * @param merchant_pub public key of the merchant (should be same for all
callbacks with the same @e cls)
- * @param wire_method which wire plugin was used
- * @param h_wire hash of wire transfer details of the merchant (should be same
for all callbacks with the same @e cls)
- * @param exec_time execution time of the wire transfer (should be same for
all callbacks with the same @e cls)
- * @param h_contract_terms which proposal was this payment about
- * @param coin_pub which public key was this payment about
- * @param deposit_value amount contributed by this coin in total
- * @param deposit_fee deposit fee charged by exchange for this coin
- */
-static void
-handle_transaction_data (void *cls,
- uint64_t rowid,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const char *wire_method,
- const struct GNUNET_HashCode *h_wire,
- struct GNUNET_TIME_Absolute exec_time,
- const struct GNUNET_HashCode *h_contract_terms,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_Amount *deposit_value,
- const struct TALER_Amount *deposit_fee)
-{
- struct WtidTransactionContext *ctx = cls;
- struct TALER_Amount delta;
- struct TEH_TrackTransferDetail *wdd;
-
- if (GNUNET_SYSERR == ctx->is_valid)
- return;
- if (GNUNET_NO == ctx->is_valid)
- {
- ctx->merchant_pub = *merchant_pub;
- ctx->h_wire = *h_wire;
- ctx->exec_time = exec_time;
- ctx->wire_method = GNUNET_strdup (wire_method);
- ctx->is_valid = GNUNET_YES;
- if (GNUNET_OK !=
- TALER_amount_subtract (&ctx->total,
- deposit_value,
- deposit_fee))
- {
- GNUNET_break (0);
- ctx->is_valid = GNUNET_SYSERR;
- return;
- }
- }
- else
- {
- if ( (0 != memcmp (&ctx->merchant_pub,
- merchant_pub,
- sizeof (struct TALER_MerchantPublicKeyP))) ||
- (0 != strcmp (wire_method,
- ctx->wire_method)) ||
- (0 != memcmp (&ctx->h_wire,
- h_wire,
- sizeof (struct GNUNET_HashCode))) )
- {
- GNUNET_break (0);
- ctx->is_valid = GNUNET_SYSERR;
- return;
- }
- if (GNUNET_OK !=
- TALER_amount_subtract (&delta,
- deposit_value,
- deposit_fee))
- {
- GNUNET_break (0);
- ctx->is_valid = GNUNET_SYSERR;
- return;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&ctx->total,
- &ctx->total,
- &delta))
- {
- GNUNET_break (0);
- ctx->is_valid = GNUNET_SYSERR;
- return;
- }
- }
- wdd = GNUNET_new (struct TEH_TrackTransferDetail);
- wdd->deposit_value = *deposit_value;
- wdd->deposit_fee = *deposit_fee;
- wdd->h_contract_terms = *h_contract_terms;
- wdd->coin_pub = *coin_pub;
- GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
- ctx->wdd_tail,
- wdd);
-}
-
-
-/**
- * Execute a "/track/transfer". Returns the transaction information
- * associated with the given wire transfer identifier.
- *
- * @param connection the MHD connection to handle
- * @param wtid wire transfer identifier to resolve
- * @return MHD result code
- */
-int
-TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
- const struct TALER_WireTransferIdentifierRawP
*wtid)
-{
- int ret;
- struct WtidTransactionContext ctx;
- struct TALER_EXCHANGEDB_Session *session;
- struct TEH_TrackTransferDetail *wdd;
- struct GNUNET_TIME_Absolute wire_fee_start_date;
- struct GNUNET_TIME_Absolute wire_fee_end_date;
- struct TALER_Amount wire_fee;
- struct TALER_MasterSignatureP wire_fee_master_sig;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- ctx.is_valid = GNUNET_NO;
- ctx.wdd_head = NULL;
- ctx.wdd_tail = NULL;
- ctx.wire_method = NULL;
- ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
- session,
- wtid,
- &handle_transaction_data,
- &ctx);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- ret = TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
- goto cleanup;
- }
- if (GNUNET_SYSERR == ctx.is_valid)
- {
- GNUNET_break (0);
- ret = TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
- goto cleanup;
- }
- if (GNUNET_NO == ctx.is_valid)
- {
- ret = TEH_RESPONSE_reply_arg_unknown (connection,
-
TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
- "wtid");
- goto cleanup;
- }
- if (GNUNET_OK !=
- TEH_plugin->get_wire_fee (TEH_plugin->cls,
- session,
- ctx.wire_method,
- ctx.exec_time,
- &wire_fee_start_date,
- &wire_fee_end_date,
- &wire_fee,
- &wire_fee_master_sig))
- {
- GNUNET_break (0);
- ret = TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
- goto cleanup;
- }
- if (GNUNET_OK !=
- TALER_amount_subtract (&ctx.total,
- &ctx.total,
- &wire_fee))
- {
- GNUNET_break (0);
- ret = TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
- goto cleanup;
- }
- ret = TEH_RESPONSE_reply_track_transfer_details (connection,
- &ctx.total,
- &ctx.merchant_pub,
- &ctx.h_wire,
- &wire_fee,
- ctx.exec_time,
- ctx.wdd_head);
- cleanup:
- while (NULL != (wdd = ctx.wdd_head))
- {
- GNUNET_CONTAINER_DLL_remove (ctx.wdd_head,
- ctx.wdd_tail,
- wdd);
- GNUNET_free (wdd);
- }
- GNUNET_free_non_null (ctx.wire_method);
- return ret;
-}
-
-
-/**
- * Closure for #handle_wtid_data.
- */
-struct DepositWtidContext
-{
-
- /**
- * Where should we send the reply?
- */
- struct MHD_Connection *connection;
-
- /**
- * Hash of the proposal data we are looking up.
- */
- struct GNUNET_HashCode h_contract_terms;
-
- /**
- * Hash of the wire transfer details we are looking up.
- */
- struct GNUNET_HashCode h_wire;
-
- /**
- * Public key we are looking up.
- */
- struct TALER_CoinSpendPublicKeyP coin_pub;
-
- /**
- * MHD result code to return.
- */
- int res;
-};
-
-
-/**
- * Function called with the results of the lookup of the
- * wire transfer identifier information.
- *
- * @param cls our context for transmission
- * @param wtid raw wire transfer identifier, NULL
- * if the transaction was not yet done
- * @param coin_contribution how much did the coin we asked about
- * contribute to the total transfer value? (deposit value including fee)
- * @param coin_fee how much did the exchange charge for the deposit fee
- * @param execution_time when was the transaction done, or
- * when we expect it to be done (if @a wtid was NULL);
- * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
- * to the exchange
- */
-static void
-handle_wtid_data (void *cls,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const struct TALER_Amount *coin_contribution,
- const struct TALER_Amount *coin_fee,
- struct GNUNET_TIME_Absolute execution_time)
-{
- struct DepositWtidContext *ctx = cls;
- struct TALER_Amount coin_delta;
-
- if (NULL == wtid)
- {
- ctx->res = TEH_RESPONSE_reply_transfer_pending (ctx->connection,
- execution_time);
- }
- else
- {
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&coin_delta,
- coin_contribution,
- coin_fee))
- {
- GNUNET_break (0);
- ctx->res = TEH_RESPONSE_reply_internal_db_error (ctx->connection,
-
TALER_EC_TRACK_TRANSACTION_DB_FEE_INCONSISTENT);
- }
- else
- {
- ctx->res = TEH_RESPONSE_reply_track_transaction (ctx->connection,
- &ctx->h_contract_terms,
- &ctx->h_wire,
- &ctx->coin_pub,
- &coin_delta,
- wtid,
- execution_time);
- }
- }
-}
-
-
-/**
- * Execute a "/track/transaction". Returns the transfer information
- * associated with the given deposit.
- *
- * @param connection the MHD connection to handle
- * @param h_contract_terms hash of the proposal data
- * @param h_wire hash of the wire details
- * @param coin_pub public key of the coin to link
- * @param merchant_pub public key of the merchant
- * @return MHD result code
- */
-int
-TEH_DB_execute_track_transaction (struct MHD_Connection *connection,
- const struct GNUNET_HashCode
*h_contract_terms,
- const struct GNUNET_HashCode *h_wire,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub,
- const struct TALER_MerchantPublicKeyP
*merchant_pub)
-{
- int ret;
- struct DepositWtidContext ctx;
- struct TALER_EXCHANGEDB_Session *session;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- ctx.connection = connection;
- ctx.h_contract_terms = *h_contract_terms;
- ctx.h_wire = *h_wire;
- ctx.coin_pub = *coin_pub;
- ctx.res = GNUNET_SYSERR;
- ret = TEH_plugin->wire_lookup_deposit_wtid (TEH_plugin->cls,
- session,
- h_contract_terms,
- h_wire,
- coin_pub,
- merchant_pub,
- &handle_wtid_data,
- &ctx);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- GNUNET_break (GNUNET_SYSERR == ctx.res);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED);
- }
- if (GNUNET_NO == ret)
- {
- GNUNET_break (GNUNET_SYSERR == ctx.res);
- return TEH_RESPONSE_reply_transaction_unknown (connection,
-
TALER_EC_TRACK_TRANSACTION_NOT_FOUND);
- }
- if (GNUNET_SYSERR == ctx.res)
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_TRACK_TRANSACTION_WTID_RESOLUTION_ERROR,
- "bug resolving deposit wtid");
- }
- return ctx.res;
-}
-
-
-/**
- * Execute a "/payback". The validity of the coin and signature have
- * already been checked. The database must now check that the coin is
- * not (double) spent, and execute the transaction (record details,
- * generate success or failure response).
- *
- * @param connection the MHD connection to handle
- * @param coin information about the coin
- * @param value how much are coins of the @a coin's denomination worth?
- * @param h_blind blinded coin to use for the lookup
- * @param coin_blind blinding factor used (for later verification by the
auditor)
- * @param coin_sig signature of the coin (to be stored)
- * @return MHD result code
- */
-int
-TEH_DB_execute_payback (struct MHD_Connection *connection,
- const struct TALER_CoinPublicInfo *coin,
- const struct TALER_Amount *value,
- const struct GNUNET_HashCode *h_blind,
- const struct TALER_DenominationBlindingKeyP
*coin_blind,
- const struct TALER_CoinSpendSignatureP *coin_sig)
-{
- int ret;
- struct TALER_EXCHANGEDB_Session *session;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_Amount amount;
- struct TALER_Amount spent;
- struct GNUNET_TIME_Absolute now;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
-
- START_TRANSACTION (session, connection);
-
- /* Check whether a payback is allowed, and if so, to which
- reserve / account the money should go */
- ret = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
- session,
- h_blind,
- &reserve_pub);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_PAYBACK_DB_FETCH_FAILED);
- }
- if (GNUNET_NO == ret)
- {
- GNUNET_break_op (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_payback_unknown (connection,
-
TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND);
- }
-
- /* Calculate remaining balance. */
- tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- session,
- &coin->coin_pub);
- TALER_amount_get_zero (value->currency,
- &spent);
- if (GNUNET_OK !=
- calculate_transaction_list_totals (tl,
- &spent,
- &spent))
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_PAYBACK_HISTORY_DB_ERROR);
- }
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&amount,
- value,
- &spent))
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE);
- }
- if ( (0 == amount.fraction) &&
- (0 == amount.value) )
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
-
TALER_EC_PAYBACK_COIN_BALANCE_ZERO,
- tl);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return ret;
- }
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- now = GNUNET_TIME_absolute_get ();
- (void) GNUNET_TIME_round_abs (&now);
-
- /* add coin to list of wire transfers for payback */
- ret = TEH_plugin->insert_payback_request (TEH_plugin->cls,
- session,
- &reserve_pub,
- coin,
- coin_sig,
- coin_blind,
- &amount,
- h_blind,
- now);
- if (GNUNET_SYSERR == ret)
- {
- TALER_LOG_WARNING ("Failed to store /payback information in database\n");
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_PAYBACK_DB_PUT_FAILED);
- }
-
- COMMIT_TRANSACTION(session, connection);
-
- return TEH_RESPONSE_reply_payback_success (connection,
- &coin->coin_pub,
- &reserve_pub,
- &amount,
- now);
-}
-
-
/* end of taler-exchange-httpd_db.c */
diff --git a/src/exchange/taler-exchange-httpd_db.h
b/src/exchange/taler-exchange-httpd_db.h
index 55faafa..7e342c8 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -24,239 +24,63 @@
#include <microhttpd.h>
#include "taler_exchangedb_plugin.h"
-
-/**
- * Execute a "/deposit". The validity of the coin and signature
- * have already been checked. The database must now check that
- * the coin is not (double or over) spent, and execute the
- * transaction (record details, generate success or failure response).
- *
- * @param connection the MHD connection to handle
- * @param deposit information about the deposit
- * @return MHD result code
- */
-int
-TEH_DB_execute_deposit (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Deposit *deposit);
-
-
-/**
- * Execute a "/refund". Returns a confirmation that the refund
- * was successful, or a failure if we are not aware of a matching
- * /deposit or if it is too late to do the refund.
- *
- * @param connection the MHD connection to handle
- * @param refund refund details
- * @return MHD result code
- */
-int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Refund *refund);
-
-
-/**
- * Execute a "/reserve/status". Given the public key of a reserve,
- * return the associated transaction history.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve to check
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_status (struct MHD_Connection *connection,
- const struct TALER_ReservePublicKeyP
*reserve_pub);
-
-
-/**
- * Execute a "/reserve/withdraw". Given a reserve and a properly signed
- * request to withdraw a coin, check the balance of the reserve and
- * if it is sufficient, store the request and return the signed
- * blinded envelope.
- *
- * @param connection the MHD connection to handle
- * @param reserve public key of the reserve
- * @param denomination_pub public key of the denomination requested
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
- const struct TALER_ReservePublicKeyP *reserve,
- const struct TALER_DenominationPublicKey
*denomination_pub,
- const char *blinded_msg,
- size_t blinded_msg_len,
- const struct TALER_ReserveSignatureP
*signature);
-
-
-/**
- * @brief Details about a melt operation of an individual coin.
- */
-struct TEH_DB_MeltDetails
-{
-
- /**
- * Information about the coin being melted.
- */
- struct TALER_CoinPublicInfo coin_info;
-
- /**
- * Signature allowing the melt (using
- * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign
over.
- */
- struct TALER_CoinSpendSignatureP melt_sig;
-
- /**
- * How much of the coin's value did the client allow to be melted?
- * This amount includes the fees, so the final amount contributed
- * to the melt is this value minus the fee for melting the coin.
- */
- struct TALER_Amount melt_amount_with_fee;
-
- /**
- * What fee is earned by the exchange? Set delayed during
- * #verify_coin_public_info().
- */
- struct TALER_Amount melt_fee;
-};
-
-
-/**
- * Execute a "/refresh/melt". We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub. Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of
y-dimension of @a commit_coin array
- * @param denom_pubs array of public denomination keys for the refresh (?)
- * @param coin_melt_detail signatures and (residual) value of and information
about the respective coin to be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to
sign
- * once the "/refres/reveal" of cut and choose is done)
- * @param transfer_pubs array of transfer public keys (what the exchange is
- * to return via "/refresh/link" to enable linkage in the
- * future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- unsigned int num_new_denoms,
- const struct TALER_DenominationPublicKey
*denom_pubs,
- const struct TEH_DB_MeltDetails *coin_melt_detail,
- struct TALER_EXCHANGEDB_RefreshCommitCoin *const*
commit_coin,
- const struct TALER_TransferPublicKeyP
*transfer_pubs);
-
-
-/**
- * Execute a "/refresh/reveal". The client is revealing to us the
- * transfer keys for #TALER_CNC_KAPPA-1 sets of coins. Verify that the
- * revealed transfer keys would allow linkage to the blinded coins,
- * and if so, return the signed coins for corresponding to the set of
- * coins that was not chosen.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash over the refresh session
- * @param transfer_privs array of length #TALER_CNC_KAPPA-1 with the revealed
transfer keys
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- struct TALER_TransferPrivateKeyP
*transfer_privs);
-
-
/**
- * Execute a "/refresh/link". Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to the
- * refreshed coin.
+ * Function implementing a database transaction. Runs the transaction
+ * logic; IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
*
- * @param connection the MHD connection to handle
- * @param coin_pub public key of the coin to link
- * @return MHD result code
+ * @param cls closure
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
*/
-int
-TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP *coin_pub);
-
-
-
-/**
- * Add an incoming transaction to the database.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve
- * @param amount amount to add to the reserve
- * @param execution_time when did we receive the wire transfer
- * @param sender_account_details which account send the funds
- * @param transfer_details information that uniquely identifies the transfer
- * @return MHD result code
- */
-int
-TEH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
- const struct TALER_ReservePublicKeyP
*reserve_pub,
- const struct TALER_Amount *amount,
- struct GNUNET_TIME_Absolute execution_time,
- const json_t *sender_account_details,
- const json_t *transfer_details);
+typedef enum GNUNET_DB_QueryStatus
+(*TEH_DB_TransactionCallback)(void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret);
/**
- * Execute a "/track/transfer". Returns the transaction information
- * associated with the given wire transfer identifier.
- *
- * @param connection the MHD connection to handle
- * @param wtid wire transfer identifier to resolve
- * @return MHD result code
- */
-int
-TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
- const struct TALER_WireTransferIdentifierRawP
*wtid);
-
-
-/**
- * Execute a "/track/transaction". Returns the transfer information
- * associated with the given deposit.
- *
- * @param connection the MHD connection to handle
- * @param h_contract_terms hash of the contract
- * @param h_wire hash of the wire details
- * @param coin_pub public key of the coin to link
- * @param merchant_pub public key of the merchant
- * @return MHD result code
+ * Run a database transaction for @a connection.
+ * Starts a transaction and calls @a cb. Upon success,
+ * attempts to commit the transaction. Upon soft failures,
+ * retries @a cb a few times. Upon hard or persistent soft
+ * errors, generates an error message for @a connection.
+ *
+ * @param connection MHD connection to run @a cb for
+ * @param[out] set to MHD response code, if transaction failed
+ * @param cb callback implementing transaction logic
+ * @param cb_cls closure for @a cb, must be read-only!
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/
int
-TEH_DB_execute_track_transaction (struct MHD_Connection *connection,
- const struct GNUNET_HashCode
*h_contract_terms,
- const struct GNUNET_HashCode *h_wire,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub,
- const struct TALER_MerchantPublicKeyP
*merchant_pub);
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+ int *mhd_ret,
+ TEH_DB_TransactionCallback cb,
+ void *cb_cls);
/**
- * Execute a "/payback". The validity of the coin and signature have
- * already been checked. The database must now check that the coin is
- * not (double) spent, and execute the transaction (record details,
- * generate success or failure response).
+ * Calculate the total value of all transactions performed.
+ * Stores @a off plus the cost of all transactions in @a tl
+ * in @a ret.
*
- * @param connection the MHD connection to handle
- * @param coin information about the coin
- * @param value how much are coins of the @a coin's denomination worth?
- * @param h_blind blinded coin to use for the lookup
- * @param coin_blind blinding factor used (for later verification by the
auditor)
- * @param coin_sig signature of the coin
- * @return MHD result code
+ * @param tl transaction list to process
+ * @param off offset to use as the starting value
+ * @param[out] ret where the resulting total is to be stored
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
+// FIXME: maybe move to another module, i.e. exchangedb???
int
-TEH_DB_execute_payback (struct MHD_Connection *connection,
- const struct TALER_CoinPublicInfo *coin,
- const struct TALER_Amount *value,
- const struct GNUNET_HashCode *h_blind,
- const struct TALER_DenominationBlindingKeyP
*coin_blind,
- const struct TALER_CoinSpendSignatureP *coin_sig);
+TEH_DB_calculate_transaction_list_totals (struct
TALER_EXCHANGEDB_TransactionList *tl,
+ const struct TALER_Amount *off,
+ struct TALER_Amount *ret);
#endif
diff --git a/src/exchange/taler-exchange-httpd_deposit.c
b/src/exchange/taler-exchange-httpd_deposit.c
index 85504d8..ccbd775 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+ Copyright (C) 2014-2017 Inria and GNUnet e.V.
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
@@ -37,6 +37,177 @@
/**
+ * Send confirmation of deposit success to client. This function
+ * will create a signed message affirming the given information
+ * and return it to the client. By this, the exchange affirms that
+ * the coin had sufficient (residual) value for the specified
+ * transaction and that it will execute the requested deposit
+ * operation with the given wiring details.
+ *
+ * @param connection connection to the client
+ * @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 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
+ * @return MHD result code
+ */
+static int
+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 refund_deadline,
+ const struct TALER_MerchantPublicKeyP *merchant,
+ const struct TALER_Amount *amount_without_fee)
+{
+ struct TALER_DepositConfirmationPS dc;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+
+ dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
+ dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
+ dc.h_contract_terms = *h_contract_terms;
+ dc.h_wire = *h_wire;
+ dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+ TALER_amount_hton (&dc.amount_without_fee,
+ amount_without_fee);
+ dc.coin_pub = *coin_pub;
+ dc.merchant = *merchant;
+ TEH_KS_sign (&dc.purpose,
+ &pub,
+ &sig);
+ return TEH_RESPONSE_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));
+}
+
+
+/**
+ * Closure for #deposit_transaction.
+ */
+struct DepositContext
+{
+ /**
+ * Information about the deposit request.
+ */
+ const struct TALER_EXCHANGEDB_Deposit *deposit;
+
+ /**
+ * Value of the coin.
+ */
+ struct TALER_Amount value;
+
+};
+
+
+/**
+ * Execute database transaction for /deposit. Runs the transaction
+ * logic; IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls a `struct 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_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct DepositContext *dc = cls;
+ const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
+ struct TALER_EXCHANGEDB_TransactionList *tl;
+ struct TALER_Amount spent;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->have_deposit (TEH_plugin->cls,
+ session,
+ deposit);
+ if (qs < 0)
+ return qs;
+ if (1 == qs)
+ {
+ struct TALER_Amount amount_without_fee;
+
+ GNUNET_assert (GNUNET_OK ==
+ 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;
+ /* add cost of all previous transactions */
+ qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+ session,
+ &deposit->coin.coin_pub,
+ &tl);
+ if (0 > qs)
+ return qs;
+ if (GNUNET_OK !=
+ TEH_DB_calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
+ {
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* Check that cost of all transactions is smaller than
+ the value of the coin. */
+ if (0 < TALER_amount_cmp (&spent,
+ &dc->value))
+ {
+ *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
+
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
+ tl);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
+ session,
+ deposit);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_DEPOSIT_STORE_DB_ERROR);
+ }
+ return qs;
+}
+
+
+/**
* We have parsed the JSON information about the deposit, do some
* basic sanity checks (especially that the signature on the coin is
* valid, and that this type of coin exists) and then execute the
@@ -51,7 +222,13 @@ verify_and_execute_deposit (struct MHD_Connection
*connection,
const struct TALER_EXCHANGEDB_Deposit *deposit)
{
struct TALER_DepositRequestPS dr;
+ int mhd_ret;
+ struct TALER_Amount amount_without_fee;
+ struct DepositContext dc;
+ struct TEH_KS_StateHandle *mks;
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+ /* check signature */
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
dr.h_contract_terms = deposit->h_contract_terms;
@@ -76,8 +253,43 @@ verify_and_execute_deposit (struct MHD_Connection
*connection,
"coin_sig");
}
- return TEH_DB_execute_deposit (connection,
- deposit);
+ /* check denomination */
+ mks = TEH_KS_acquire ();
+ dki = TEH_KS_denomination_key_lookup (mks,
+ &deposit->coin.denom_pub,
+ TEH_KS_DKU_DEPOSIT);
+ if (NULL == dki)
+ {
+ TEH_KS_release (mks);
+ return TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
+ }
+ TALER_amount_ntoh (&dc.value,
+ &dki->issue.properties.value);
+ TEH_KS_release (mks);
+
+ /* execute transaction */
+ dc.deposit = deposit;
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &deposit_transaction,
+ &dc))
+ return mhd_ret;
+
+ /* generate regular response */
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&amount_without_fee,
+ &deposit->amount_with_fee,
+ &deposit->deposit_fee));
+ return 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);
}
@@ -199,6 +411,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
/* FIXME: #3887: if DK was revoked, we might want to give a 403 and not a
404! */
TEH_KS_release (key_state);
TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n");
+ GNUNET_JSON_parse_free (spec);
return TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_DEPOSIT_DENOMINATION_KEY_UNKNOWN,
"denom_pub");
@@ -211,6 +424,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
{
TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
TEH_KS_release (key_state);
+ GNUNET_JSON_parse_free (spec);
return TEH_RESPONSE_reply_signature_invalid (connection,
TALER_EC_DEPOSIT_DENOMINATION_SIGNATURE_INVALID,
"ub_sig");
@@ -223,6 +437,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
&deposit.amount_with_fee))
{
GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
return TEH_RESPONSE_reply_external_error (connection,
TALER_EC_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
"deposited amount smaller than
depositing fee");
diff --git a/src/exchange/taler-exchange-httpd_keystate.c
b/src/exchange/taler-exchange-httpd_keystate.c
index 3f81b2e..ec0a470 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -308,6 +308,81 @@ handle_signal (int signal_number)
/**
+ * Closure for #add_revocations_transaction().
+ */
+struct AddRevocationContext
+{
+ /**
+ * Denomination key that is revoked.
+ */
+ const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+
+ /**
+ * Signature affirming the revocation.
+ */
+ const struct TALER_MasterSignatureP *revocation_master_sig;
+};
+
+
+/**
+ * Execute transaction to add revocations.
+ *
+ * @param cls closure with the `struct AddRevocationContext *`
+ * @param connection NULL
+ * @param session database session to use
+ * @param[out] mhd_ret NULL
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_revocations_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct AddRevocationContext *arc = cls;
+
+ return TEH_plugin->insert_denomination_revocation (TEH_plugin->cls,
+ session,
+
&arc->dki->issue.properties.denom_hash,
+
arc->revocation_master_sig);
+}
+
+
+/**
+ * Execute transaction to add a denomination to the DB.
+ *
+ * @param cls closure with the `const struct
TALER_EXCHANGEDB_DenominationKeyIssueInformation *`
+ * @param connection NULL
+ * @param session database session to use
+ * @param[out] mhd_ret NULL
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_denomination_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_EXCHANGEDB_DenominationKeyInformationP issue_exists;
+
+ qs = TEH_plugin->get_denomination_info (TEH_plugin->cls,
+ session,
+ &dki->denom_pub,
+ &issue_exists);
+ if (0 > qs)
+ return qs;
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ return qs;
+ return TEH_plugin->insert_denomination_info (TEH_plugin->cls,
+ session,
+ &dki->denom_pub,
+ &dki->issue);
+}
+
+
+/**
* Iterator for (re)loading/initializing denomination keys.
*
* @param cls closure
@@ -330,7 +405,6 @@ reload_keys_denom_iter (void *cls,
struct GNUNET_TIME_Absolute horizon;
struct GNUNET_TIME_Absolute expire_deposit;
struct GNUNET_HashCode denom_key_hash;
- struct TALER_EXCHANGEDB_Session *session;
int res;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -355,16 +429,12 @@ reload_keys_denom_iter (void *cls,
return GNUNET_OK;
}
- session = TEH_plugin->get_session (TEH_plugin->cls);
- if (NULL == session)
- return GNUNET_SYSERR;
-
if (NULL != revocation_master_sig)
{
- unsigned int thresh = 0;
-
+ struct AddRevocationContext arc;
+
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Adding denomination key `%s' to revokation set\n",
+ "Adding denomination key `%s' to revocation set\n",
alias);
res = store_in_map (ctx->revoked_map,
dki);
@@ -373,45 +443,20 @@ reload_keys_denom_iter (void *cls,
/* Try to insert DKI into DB until we succeed; note that if the DB
failure is persistent, we need to die, as we cannot continue
without the DKI being in the DB). */
- res = GNUNET_SYSERR;
- while (GNUNET_OK != res)
+ arc.dki = dki;
+ arc.revocation_master_sig = revocation_master_sig;
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (NULL,
+ NULL,
+ &add_revocations_transaction,
+ &arc))
{
- thresh++;
- if (thresh > 16)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Giving up, this is fatal. Committing suicide via
SIGTERM.\n");
- handle_signal (SIGTERM);
- return GNUNET_SYSERR;
- }
- res = TEH_plugin->start (TEH_plugin->cls,
- session);
- if (GNUNET_OK != res)
- {
- /* Transaction start failed!? Very bad error, log and retry */
- GNUNET_break (0);
- continue;
- }
- res = TEH_plugin->insert_denomination_revocation (TEH_plugin->cls,
- session,
-
&dki->issue.properties.denom_hash,
- revocation_master_sig);
- if (GNUNET_SYSERR == res)
- {
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- continue;
- }
- if (GNUNET_NO == res)
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- break; /* already in is also OK! */
- }
- res = TEH_plugin->commit (TEH_plugin->cls,
- session);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Giving up, this is fatal. Committing suicide via
SIGTERM.\n");
+ handle_signal (SIGTERM);
+ return GNUNET_SYSERR;
}
+
GNUNET_assert (0 ==
json_array_append_new (ctx->payback_array,
GNUNET_JSON_from_data_auto
(&dki->issue.properties.denom_hash)));
@@ -435,63 +480,16 @@ reload_keys_denom_iter (void *cls,
sizeof (struct GNUNET_HashCode));
-
- session = TEH_plugin->get_session (TEH_plugin->cls);
- if (NULL == session)
- return GNUNET_SYSERR;
- /* Try to insert DKI into DB until we succeed; note that if the DB
- failure is persistent, this code may loop forever (as there is no
- sane alternative, we cannot continue without the DKI being in the
- DB). */
- res = GNUNET_SYSERR;
- while (GNUNET_OK != res)
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (NULL,
+ NULL,
+ &add_denomination_transaction,
+ (void *) dki))
{
- res = TEH_plugin->start (TEH_plugin->cls,
- session);
- if (GNUNET_OK != res)
- {
- /* Transaction start failed!? Very bad error, log and retry */
- GNUNET_break (0);
- continue;
- }
- res = TEH_plugin->get_denomination_info (TEH_plugin->cls,
- session,
- &dki->denom_pub,
- NULL);
- if (GNUNET_SYSERR == res)
- {
- /* Fetch failed!? Very bad error, log and retry */
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- continue;
- }
- if (GNUNET_OK == res)
- {
- /* Record exists, we're good, just exit */
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- break;
- }
- res = TEH_plugin->insert_denomination_info (TEH_plugin->cls,
- session,
- &dki->denom_pub,
- &dki->issue);
- if (GNUNET_OK != res)
- {
- /* Insert failed!? Very bad error, log and retry */
- GNUNET_break (0);
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- continue;
- }
- res = TEH_plugin->commit (TEH_plugin->cls,
- session);
- /* If commit succeeded, we're done, otherwise we retry; this
- time without logging, as theroetically commits can fail
- in a transactional DB due to concurrent activities that
- cannot be reconciled. This should be rare for DKIs, but
- as it is possible we just retry until we succeed. */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Giving up, this is fatal. Committing suicide via SIGTERM.\n");
+ handle_signal (SIGTERM);
+ return GNUNET_SYSERR;
}
res = store_in_map (ctx->denomkey_map,
@@ -663,7 +661,6 @@ reload_auditor_iter (void *cls,
const struct TALER_DenominationKeyValidityPS *dki)
{
struct TEH_KS_StateHandle *ctx = cls;
- unsigned int i;
unsigned int keep;
const struct TALER_AuditorSignatureP *kept_asigs[dki_len];
const struct TALER_DenominationKeyValidityPS *kept_dkis[dki_len];
@@ -680,7 +677,7 @@ reload_auditor_iter (void *cls,
/* Filter the auditor information for those for which the
keys actually match the denomination keys that are active right now */
keep = 0;
- for (i=0;i<dki_len;i++)
+ for (unsigned int i=0;i<dki_len;i++)
{
if (GNUNET_YES ==
GNUNET_CONTAINER_multihashmap_contains (ctx->denomkey_map,
@@ -1276,8 +1273,36 @@ TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
char *json;
size_t json_len;
int comp;
+ const char *have;
+ struct GNUNET_TIME_Absolute last_issue_date;
+ have = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "last_issue_date");
+ if (NULL != have)
+ {
+ unsigned long long haven;
+
+ if (1 !=
+ sscanf (have,
+ "%llu",
+ &haven))
+ {
+ GNUNET_break_op (0);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_KEYS_HAVE_NOT_NUMERIC,
+ "have");
+ }
+ last_issue_date.abs_value_us = (uint64_t) haven;
+ }
+ else
+ {
+ last_issue_date.abs_value_us = 0LLU;
+ }
+
key_state = TEH_KS_acquire ();
+ /* FIXME: #4840: compute /keys delta from last_issue_date */
+ (void) last_issue_date;
comp = MHD_NO;
if (NULL != key_state->keys_jsonz)
comp = TEH_RESPONSE_can_compress (connection);
diff --git a/src/exchange/taler-exchange-httpd_payback.c
b/src/exchange/taler-exchange-httpd_payback.c
index 7c5230d..8b4051c 100644
--- a/src/exchange/taler-exchange-httpd_payback.c
+++ b/src/exchange/taler-exchange-httpd_payback.c
@@ -35,6 +35,253 @@
/**
+ * A wallet asked for /payback, but we do not know anything about the
+ * original withdraw operation specified. Generates a 404 reply.
+ *
+ * @param connection connection to the client
+ * @param ec Taler error code
+ * @return MHD result code
+ */
+static int
+reply_payback_unknown (struct MHD_Connection *connection,
+ enum TALER_ErrorCode ec)
+{
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s, s:I}",
+ "error", "blinded coin unknown",
+ "code", (json_int_t) ec);
+}
+
+
+/**
+ * A wallet asked for /payback, return the successful response.
+ *
+ * @param connection connection to the client
+ * @param coin_pub coin for which we are processing the payback request
+ * @param reserve_pub public key of the reserve that will receive the payback
+ * @param amount the amount we will wire back
+ * @param timestamp when did the exchange receive the /payback request
+ * @return MHD result code
+ */
+static int
+reply_payback_success (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *amount,
+ struct GNUNET_TIME_Absolute timestamp)
+{
+ struct TALER_PaybackConfirmationPS pc;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+
+ pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
+ pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
+ pc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ TALER_amount_hton (&pc.payback_amount,
+ amount);
+ pc.coin_pub = *coin_pub;
+ pc.reserve_pub = *reserve_pub;
+ TEH_KS_sign (&pc.purpose,
+ &pub,
+ &sig);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o, s:o, s:o, s:o}",
+ "reserve_pub",
GNUNET_JSON_from_data_auto (reserve_pub),
+ "timestamp", GNUNET_JSON_from_time_abs
(timestamp),
+ "amount", TALER_JSON_from_amount
(amount),
+ "exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
+ "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #payback_transaction.
+ */
+struct PaybackContext
+{
+ /**
+ * Hash of the blinded coin.
+ */
+ struct GNUNET_HashCode h_blind;
+
+ /**
+ * Full value of the coin.
+ */
+ struct TALER_Amount value;
+
+ /**
+ * Details about the coin.
+ */
+ const struct TALER_CoinPublicInfo *coin;
+
+ /**
+ * Key used to blind the coin.
+ */
+ const struct TALER_DenominationBlindingKeyP *coin_bks;
+
+ /**
+ * Signature of the coin requesting payback.
+ */
+ const struct TALER_CoinSpendSignatureP *coin_sig;
+
+ /**
+ * Set by #payback_transaction() to the reserve that will
+ * receive the payback.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Set by #payback_transaction() to the amount that will be paid back
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Set by #payback_transaction to the timestamp when the payback
+ * was accepted.
+ */
+ struct GNUNET_TIME_Absolute now;
+
+};
+
+
+/**
+ * Execute a "/payback". The validity of the coin and signature have
+ * already been checked. The database must now check that the coin is
+ * not (double) spent, and execute the transaction.
+ *
+ * IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls the `struct PaybackContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+payback_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct PaybackContext *pc = cls;
+ struct TALER_EXCHANGEDB_TransactionList *tl;
+ struct TALER_Amount spent;
+ enum GNUNET_DB_QueryStatus qs;
+
+ /* Check whether a payback is allowed, and if so, to which
+ reserve / account the money should go */
+ qs = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
+ session,
+ &pc->h_blind,
+ &pc->reserve_pub);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_PAYBACK_DB_FETCH_FAILED);
+ }
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ GNUNET_break_op (0);
+ *mhd_ret = reply_payback_unknown (connection,
+ TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* Calculate remaining balance. */
+ qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+ session,
+ &pc->coin->coin_pub,
+ &tl);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_PAYBACK_DB_FETCH_FAILED);
+ }
+ return qs;
+ }
+ TALER_amount_get_zero (pc->value.currency,
+ &spent);
+ if (GNUNET_OK !=
+ TEH_DB_calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
+ {
+ GNUNET_break (0);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_PAYBACK_HISTORY_DB_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&pc->amount,
+ &pc->value,
+ &spent))
+ {
+ GNUNET_break (0);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if ( (0 == pc->amount.fraction) &&
+ (0 == pc->amount.value) )
+ {
+ TEH_plugin->rollback (TEH_plugin->cls,
+ session);
+ *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
+
TALER_EC_PAYBACK_COIN_BALANCE_ZERO,
+ tl);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ pc->now = GNUNET_TIME_absolute_get ();
+ (void) GNUNET_TIME_round_abs (&pc->now);
+
+ /* add coin to list of wire transfers for payback */
+ qs = TEH_plugin->insert_payback_request (TEH_plugin->cls,
+ session,
+ &pc->reserve_pub,
+ pc->coin,
+ pc->coin_sig,
+ pc->coin_bks,
+ &pc->amount,
+ &pc->h_blind,
+ pc->now);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ TALER_LOG_WARNING ("Failed to store /payback information in database\n");
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_PAYBACK_DB_PUT_FAILED);
+ }
+ return qs;
+ }
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
* We have parsed the JSON information about the payback request. Do
* some basic sanity checks (especially that the signature on the
* request and coin is valid) and then execute the payback operation.
@@ -53,14 +300,14 @@ verify_and_execute_payback (struct MHD_Connection
*connection,
const struct TALER_DenominationBlindingKeyP
*coin_bks,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
+ struct PaybackContext pc;
struct TEH_KS_StateHandle *key_state;
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TALER_PaybackRequestPS pr;
- struct TALER_Amount value;
- struct GNUNET_HashCode h_blind;
struct GNUNET_HashCode c_hash;
char *coin_ev;
size_t coin_ev_size;
+ int mhd_ret;
/* check denomination exists and is in payback mode */
key_state = TEH_KS_acquire ();
@@ -75,7 +322,7 @@ verify_and_execute_payback (struct MHD_Connection
*connection,
TALER_EC_PAYBACK_DENOMINATION_KEY_UNKNOWN,
"denom_pub");
}
- TALER_amount_ntoh (&value,
+ TALER_amount_ntoh (&pc.value,
&dki->issue.properties.value);
/* check denomination signature */
@@ -127,15 +374,24 @@ verify_and_execute_payback (struct MHD_Connection
*connection,
}
GNUNET_CRYPTO_hash (coin_ev,
coin_ev_size,
- &h_blind);
+ &pc.h_blind);
GNUNET_free (coin_ev);
- return TEH_DB_execute_payback (connection,
- coin,
- &value,
- &h_blind,
- coin_bks,
- coin_sig);
+ pc.coin_sig = coin_sig;
+ pc.coin_bks = coin_bks;
+ pc.coin = coin;
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &payback_transaction,
+ &pc))
+ return mhd_ret;
+
+ return reply_payback_success (connection,
+ &coin->coin_pub,
+ &pc.reserve_pub,
+ &pc.amount,
+ pc.now);
}
diff --git a/src/exchange/taler-exchange-httpd_refresh.c
b/src/exchange/taler-exchange-httpd_refresh.c
deleted file mode 100644
index 3a8875f..0000000
--- a/src/exchange/taler-exchange-httpd_refresh.c
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014, 2015, 2016 Inria & GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free
Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
-
- You should have received a copy of the GNU Affero General Public License
along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_refresh.c
- * @brief Handle /refresh/ requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
-
-
-/**
- * Handle a "/refresh/melt" request after the main JSON parsing has happened.
- * We now need to validate the coins being melted and the session signature
- * and then hand things of to execute the melt operation.
- *
- * @param connection the MHD connection to handle
- * @param num_new_denoms number of coins to be created, size of y-dimension of
@a commit_link array
- * @param denom_pubs array of @a num_new_denoms keys
- * @param coin_melt_details melting details
- * @param session_hash hash over the data that the client commits to
- * @param commit_coin 2d array of coin commitments (what the exchange is to
sign
- * once the "/refres/reveal" of cut and choose is done)
- * @param transfer_pubs array of transfer public keys (which the exchange is
- * to return via "/refresh/link" to enable linkage in the
- * future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-static int
-handle_refresh_melt_binary (struct MHD_Connection *connection,
- unsigned int num_new_denoms,
- const struct TALER_DenominationPublicKey
*denom_pubs,
- const struct TEH_DB_MeltDetails *coin_melt_details,
- const struct GNUNET_HashCode *session_hash,
- struct TALER_EXCHANGEDB_RefreshCommitCoin *const*
commit_coin,
- const struct TALER_TransferPublicKeyP
*transfer_pubs)
-{
- unsigned int i;
- struct TEH_KS_StateHandle *key_state;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
- struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
- struct TALER_Amount cost;
- struct TALER_Amount total_cost;
- struct TALER_Amount value;
- struct TALER_Amount fee_withdraw;
- struct TALER_Amount fee_melt;
- struct TALER_Amount total_melt;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "/refresh/melt request for session %s\n",
- GNUNET_h2s (session_hash));
-
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (TEH_exchange_currency_string,
- &total_cost));
- key_state = TEH_KS_acquire ();
- for (i=0;i<num_new_denoms;i++)
- {
- dk = TEH_KS_denomination_key_lookup (key_state,
- &denom_pubs[i],
- TEH_KS_DKU_WITHDRAW);
- if (NULL == dk)
- {
- GNUNET_break_op (0);
- TEH_KS_release (key_state);
- return TEH_RESPONSE_reply_arg_invalid (connection,
-
TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND,
- "new_denoms");
- }
- dki = &dk->issue;
- TALER_amount_ntoh (&value,
- &dki->properties.value);
- TALER_amount_ntoh (&fee_withdraw,
- &dki->properties.fee_withdraw);
- if ( (GNUNET_OK !=
- TALER_amount_add (&cost,
- &value,
- &fee_withdraw)) ||
- (GNUNET_OK !=
- TALER_amount_add (&total_cost,
- &cost,
- &total_cost)) )
- {
- GNUNET_break_op (0);
- TEH_KS_release (key_state);
- return TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW,
- "cost calculation failure");
- }
- }
-
- dk = TEH_KS_denomination_key_lookup (key_state,
- &coin_melt_details->coin_info.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == dk)
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_arg_unknown (connection,
-
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
- "denom_pub");
- }
- dki = &dk->issue;
- TALER_amount_ntoh (&fee_melt,
- &dki->properties.fee_refresh);
- if (GNUNET_OK !=
- TALER_amount_subtract (&total_melt,
- &coin_melt_details->melt_amount_with_fee,
- &fee_melt))
- {
- GNUNET_break_op (0);
- TEH_KS_release (key_state);
- return TEH_RESPONSE_reply_external_error (connection,
-
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
- "Melt contribution below melting
fee");
- }
- TEH_KS_release (key_state);
- if (0 !=
- TALER_amount_cmp (&total_cost,
- &total_melt))
- {
- GNUNET_break_op (0);
- /* We require total value of coins being melted and
- total value of coins being generated to match! */
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:I}",
- "error", "value mismatch",
- "code", (json_int_t)
TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
- }
- return TEH_DB_execute_refresh_melt (connection,
- session_hash,
- num_new_denoms,
- denom_pubs,
- coin_melt_details,
- commit_coin,
- transfer_pubs);
-}
-
-
-/**
- * Extract public coin information from a JSON object.
- *
- * @param connection the connection to send error responses to
- * @param coin_info the JSON object to extract the coin info from
- * @param[out] r_melt_detail set to details about the coin's melting
permission (if valid)
- * @return #GNUNET_YES if coin public info in JSON was valid
- * #GNUNET_NO JSON was invalid, response was generated
- * #GNUNET_SYSERR on internal error
- */
-static int
-get_coin_public_info (struct MHD_Connection *connection,
- const json_t *coin_info,
- struct TEH_DB_MeltDetails *r_melt_detail)
-{
- int ret;
- struct TALER_CoinSpendSignatureP melt_sig;
- struct TALER_DenominationSignature sig;
- struct TALER_DenominationPublicKey pk;
- struct TALER_Amount amount;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("coin_pub",
&r_melt_detail->coin_info.coin_pub),
- TALER_JSON_spec_denomination_signature ("denom_sig", &sig),
- TALER_JSON_spec_denomination_public_key ("denom_pub", &pk),
- GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig),
- TALER_JSON_spec_amount ("value_with_fee", &amount),
- GNUNET_JSON_spec_end ()
- };
-
- ret = TEH_PARSE_json_data (connection,
- coin_info,
- spec);
- if (GNUNET_OK != ret)
- {
- GNUNET_break_op (0);
- return ret;
- }
- /* check exchange signature on the coin */
- r_melt_detail->coin_info.denom_sig = sig;
- r_melt_detail->coin_info.denom_pub = pk;
- if (GNUNET_OK !=
- TALER_test_coin_valid (&r_melt_detail->coin_info))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- r_melt_detail->coin_info.denom_sig.rsa_signature = NULL;
- r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL;
- return (MHD_YES ==
- TEH_RESPONSE_reply_signature_invalid (connection,
-
TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
- "denom_sig"))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- r_melt_detail->melt_sig = melt_sig;
- r_melt_detail->melt_amount_with_fee = amount;
- return GNUNET_OK;
-}
-
-
-/**
- * Verify that the signature shows that this coin is to be melted into
- * the given @a session_hash melting session, and that this is a valid
- * coin (we know the denomination key and the signature on it is
- * valid). Essentially, this does all of the per-coin checks that can
- * be done before the transaction starts.
- *
- * @param connection the connection to send error responses to
- * @param session_hash hash over refresh session the coin is melted into
- * @param[in,out] melt_detail details about the coin's melting permission,
- * the `melt_fee` is updated
- * @return #GNUNET_YES if coin public info in JSON was valid
- * #GNUNET_NO JSON was invalid, response was generated
- * #GNUNET_SYSERR on internal error
- */
-static int
-verify_coin_public_info (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- struct TEH_DB_MeltDetails *melt_detail)
-{
- struct TALER_RefreshMeltCoinAffirmationPS body;
- struct TEH_KS_StateHandle *key_state;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- struct TALER_Amount fee_refresh;
-
- /* FIXME: we lookup the dki twice during /refresh/melt.
- This should be avoided. */
- key_state = TEH_KS_acquire ();
- dki = TEH_KS_denomination_key_lookup (key_state,
- &melt_detail->coin_info.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == dki)
- {
- TEH_KS_release (key_state);
- TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
- return TEH_RESPONSE_reply_arg_unknown (connection,
-
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
- "denom_pub");
- }
- TALER_amount_ntoh (&fee_refresh,
- &dki->issue.properties.fee_refresh);
- melt_detail->melt_fee = fee_refresh;
- body.purpose.size = htonl (sizeof (struct
TALER_RefreshMeltCoinAffirmationPS));
- body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
- body.session_hash = *session_hash;
- TALER_amount_hton (&body.amount_with_fee,
- &melt_detail->melt_amount_with_fee);
- TALER_amount_hton (&body.melt_fee,
- &fee_refresh);
- body.coin_pub = melt_detail->coin_info.coin_pub;
- if (TALER_amount_cmp (&fee_refresh,
- &melt_detail->melt_amount_with_fee) > 0)
- {
- GNUNET_break_op (0);
- TEH_KS_release (key_state);
- return (MHD_YES ==
- TEH_RESPONSE_reply_external_error (connection,
-
TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
- "melt amount smaller than
melting fee"))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
-
- TEH_KS_release (key_state);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
- &body.purpose,
- &melt_detail->melt_sig.eddsa_signature,
- &melt_detail->coin_info.coin_pub.eddsa_pub))
- {
- GNUNET_break_op (0);
- if (MHD_YES !=
- TEH_RESPONSE_reply_signature_invalid (connection,
-
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
- "confirm_sig"))
- return GNUNET_SYSERR;
- return GNUNET_NO;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Release memory from the @a commit_coin array.
- *
- * @param commit_coin array to release
- * @param kappa size of 1st dimension
- * @param num_new_coins size of 2nd dimension
- */
-static void
-free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
- unsigned int kappa,
- unsigned int num_new_coins)
-{
- unsigned int i;
- unsigned int j;
-
- for (i=0;i<kappa;i++)
- {
- if (NULL == commit_coin[i])
- break;
- for (j=0;j<num_new_coins;j++)
- GNUNET_free_non_null (commit_coin[i][j].coin_ev);
- GNUNET_free (commit_coin[i]);
- }
-}
-
-
-/**
- * Handle a "/refresh/melt" request after the first parsing has happened.
- * We now need to validate the coins being melted and the session signature
- * and then hand things of to execute the melt operation. This function
- * parses the JSON arrays and then passes processing on to
- * #handle_refresh_melt_binary().
- *
- * @param connection the MHD connection to handle
- * @param new_denoms array of denomination keys
- * @param melt_coin coin to melt
- * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of transfer keys
- * @param coin_evs #TALER_CNC_KAPPA-dimensional array of envelopes to sign
- * @return MHD result code
- */
-static int
-handle_refresh_melt_json (struct MHD_Connection *connection,
- const json_t *new_denoms,
- const json_t *melt_coin,
- const json_t *transfer_pubs,
- const json_t *coin_evs)
-{
- int res;
- unsigned int i;
- unsigned int j;
- struct TALER_DenominationPublicKey *denom_pubs;
- unsigned int num_newcoins;
- struct TEH_DB_MeltDetails coin_melt_details;
- struct GNUNET_HashCode session_hash;
- struct GNUNET_HashContext *hash_context;
- struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
- struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
-
- /* For the signature check, we hash most of the inputs together
- (except for the signatures on the coins). */
- hash_context = GNUNET_CRYPTO_hash_context_start ();
-
- for (i = 0; i < TALER_CNC_KAPPA; i++)
- {
- struct GNUNET_JSON_Specification trans_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &transfer_pub[i]),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_json_array (connection,
- transfer_pubs,
- trans_spec,
- i, -1);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- goto cleanup_hc;
- }
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &transfer_pub[i],
- sizeof (struct TALER_TransferPublicKeyP));
- }
-
-
- num_newcoins = json_array_size (new_denoms);
- denom_pubs = GNUNET_new_array (num_newcoins,
- struct TALER_DenominationPublicKey);
- for (i=0;i<num_newcoins;i++)
- {
- char *buf;
- size_t buf_size;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_denomination_public_key (NULL,
- &denom_pubs[i]),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_json_array (connection,
- new_denoms,
- spec,
- i, -1);
- if (GNUNET_OK != res)
- {
- res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- goto cleanup_denoms;
- }
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode
(denom_pubs[i].rsa_public_key,
- &buf);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- buf,
- buf_size);
- GNUNET_free (buf);
- }
-
- {
- /* decode JSON data on coin to melt */
- struct TALER_AmountNBO melt_amount;
-
- res = get_coin_public_info (connection,
- melt_coin,
- &coin_melt_details);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- goto cleanup_melt_details;
- }
- TALER_amount_hton (&melt_amount,
- &coin_melt_details.melt_amount_with_fee);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &coin_melt_details.coin_info.coin_pub,
- sizeof (struct
TALER_CoinSpendPublicKeyP));
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &melt_amount,
- sizeof (struct TALER_AmountNBO));
- }
-
- /* parse JSON arrays into binary arrays and hash everything
- together for the signature check */
- memset (commit_coin,
- 0,
- sizeof (commit_coin));
- for (i = 0; i < TALER_CNC_KAPPA; i++)
- {
- commit_coin[i] = GNUNET_new_array (num_newcoins,
- struct
TALER_EXCHANGEDB_RefreshCommitCoin);
- for (j = 0; j < num_newcoins; j++)
- {
- struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &commit_coin[i][j];
- struct GNUNET_JSON_Specification coin_spec[] = {
- GNUNET_JSON_spec_varsize (NULL,
- (void **) &rcc->coin_ev,
- &rcc->coin_ev_size),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_json_array (connection,
- coin_evs,
- coin_spec,
- i, j, -1);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- goto cleanup;
- }
-
- GNUNET_CRYPTO_hash_context_read (hash_context,
- rcc->coin_ev,
- rcc->coin_ev_size);
- }
- }
-
- GNUNET_CRYPTO_hash_context_finish (hash_context,
- &session_hash);
- hash_context = NULL;
- /* verify signature on coins to melt */
- res = verify_coin_public_info (connection,
- &session_hash,
- &coin_melt_details);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- goto cleanup;
- }
-
- /* execute commit */
- res = handle_refresh_melt_binary (connection,
- num_newcoins,
- denom_pubs,
- &coin_melt_details,
- &session_hash,
- commit_coin,
- transfer_pub);
- cleanup:
- free_commit_coins (commit_coin,
- TALER_CNC_KAPPA,
- num_newcoins);
- cleanup_melt_details:
- if (NULL != coin_melt_details.coin_info.denom_pub.rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free
(coin_melt_details.coin_info.denom_pub.rsa_public_key);
- if (NULL != coin_melt_details.coin_info.denom_sig.rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free
(coin_melt_details.coin_info.denom_sig.rsa_signature);
- cleanup_denoms:
- if (NULL != denom_pubs)
- {
- for (j=0;j<num_newcoins;j++)
- if (NULL != denom_pubs[j].rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
- GNUNET_free (denom_pubs);
- }
- cleanup_hc:
- if (NULL != hash_context)
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- return res;
-}
-
-
-/**
- * Handle a "/refresh/melt" request. Parses the request into the JSON
- * components and then hands things of to #handle_refresh_melt_json()
- * to validate the melted coins, the signature and execute the melt
- * using TEH_DB_execute_refresh_melt().
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- json_t *root;
- json_t *new_denoms;
- json_t *melt_coin;
- json_t *coin_evs;
- json_t *transfer_pubs;
- int res;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("new_denoms", &new_denoms),
- GNUNET_JSON_spec_json ("melt_coin", &melt_coin),
- GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
- GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- if ( (GNUNET_NO == res) || (NULL == root) )
- return MHD_YES;
-
- res = TEH_PARSE_json_data (connection,
- root,
- spec);
- json_decref (root);
- if (GNUNET_OK != res)
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-
- /* Determine dimensionality of the request (kappa, #old and #new coins) */
- if (TALER_CNC_KAPPA != json_array_size (coin_evs))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_arg_invalid (connection,
-
TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID,
- "coin_evs");
- }
- if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_arg_invalid (connection,
-
TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID,
- "transfer_pubs");
- }
- res = handle_refresh_melt_json (connection,
- new_denoms,
- melt_coin,
- transfer_pubs,
- coin_evs);
- GNUNET_JSON_parse_free (spec);
- return res;
-}
-
-
-/**
- * Handle a "/refresh/reveal" request. Parses the given JSON
- * transfer private keys and if successful, passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash identifying the melting session
- * @param tp_json private transfer keys in JSON format
- * @return MHD result code
- */
-static int
-handle_refresh_reveal_json (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- const json_t *tp_json)
-{
- struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
- unsigned int i;
- int res;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "reveal request for session %s\n",
- GNUNET_h2s (session_hash));
-
- res = GNUNET_OK;
- for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
- {
- struct GNUNET_JSON_Specification tp_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &transfer_privs[i]),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK != res)
- break;
- res = TEH_PARSE_json_array (connection,
- tp_json,
- tp_spec,
- i, -1);
- GNUNET_break_op (GNUNET_OK == res);
- }
- if (GNUNET_OK != res)
- res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- else
- res = TEH_DB_execute_refresh_reveal (connection,
- session_hash,
- transfer_privs);
- return res;
-}
-
-
-/**
- * Handle a "/refresh/reveal" request. This time, the client reveals
- * the private transfer keys except for the cut-and-choose value
- * returned from "/refresh/melt". This function parses the revealed
- * keys and secrets and ultimately passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- struct GNUNET_HashCode session_hash;
- int res;
- json_t *root;
- json_t *transfer_privs;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
- GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- if ( (GNUNET_NO == res) || (NULL == root) )
- return MHD_YES;
-
- res = TEH_PARSE_json_data (connection,
- root,
- spec);
- json_decref (root);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- }
- /* Determine dimensionality of the request (kappa and #old coins) */
- /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
- if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
- {
- GNUNET_JSON_parse_free (spec);
- GNUNET_break_op (0);
- return TEH_RESPONSE_reply_arg_invalid (connection,
-
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
- "transfer_privs");
- }
- res = handle_refresh_reveal_json (connection,
- &session_hash,
- transfer_privs);
- GNUNET_JSON_parse_free (spec);
- return res;
-}
-
-
-/**
- * Handle a "/refresh/link" request. Note that for "/refresh/link"
- * we do use a simple HTTP GET, and a HTTP POST!
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- struct TALER_CoinSpendPublicKeyP coin_pub;
- int res;
-
- res = TEH_PARSE_mhd_request_arg_data (connection,
- "coin_pub",
- &coin_pub,
- sizeof (struct
TALER_CoinSpendPublicKeyP));
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- if (GNUNET_OK != res)
- return MHD_YES;
- return TEH_DB_execute_refresh_link (connection,
- &coin_pub);
-}
-
-
-/* end of taler-exchange-httpd_refresh.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh.h
b/src/exchange/taler-exchange-httpd_refresh.h
deleted file mode 100644
index 61b3b4f..0000000
--- a/src/exchange/taler-exchange-httpd_refresh.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free
Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
-
- You should have received a copy of the GNU Affero General Public License
along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_refresh.h
- * @brief Handle /refresh/ requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_REFRESH_H
-#define TALER_EXCHANGE_HTTPD_REFRESH_H
-
-#include <gnunet/gnunet_util_lib.h>
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-
-/**
- * Handle a "/refresh/melt" request. Parses the request into the JSON
- * components and then hands things of to #handle_refresh_melt_json()
- * to validate the melted coins, the signature and execute the melt
- * using TEH_DB_execute_refresh_melt().
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
-
-/**
- * Handle a "/refresh/reveal" request. This time, the client reveals
- * the private transfer keys except for the cut-and-choose value
- * returned from "/refresh/commit". This function parses the revealed
- * keys and secrets and ultimately passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
-
-/**
- * Handle a "/refresh/link" request
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
-
-#endif
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c
b/src/exchange/taler-exchange-httpd_refresh_link.c
new file mode 100644
index 0000000..0be6998
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_link.c
@@ -0,0 +1,326 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 Inria & GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_link.c
+ * @brief Handle /refresh/link requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * @brief Information for each session a coin was melted into.
+ */
+struct TEH_RESPONSE_LinkSessionInfo
+{
+ /**
+ * Transfer public key of the coin.
+ */
+ struct TALER_TransferPublicKeyP transfer_pub;
+
+ /**
+ * Linked data of coins being created in the session.
+ */
+ struct TALER_EXCHANGEDB_LinkDataList *ldl;
+
+};
+
+
+/**
+ * Closure for #handle_transfer_data().
+ */
+struct HTD_Context
+{
+
+ /**
+ * Public key of the coin that we are tracing.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Session link data we collect.
+ */
+ struct TEH_RESPONSE_LinkSessionInfo *sessions;
+
+ /**
+ * Database session. Nothing to do with @a sessions.
+ */
+ struct TALER_EXCHANGEDB_Session *session;
+
+ /**
+ * MHD connection, for queueing replies.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Number of sessions the coin was melted into.
+ */
+ unsigned int num_sessions;
+
+ /**
+ * How are we expected to proceed. #GNUNET_SYSERR if we
+ * failed to return an error (should return #MHD_NO).
+ * #GNUNET_NO if we succeeded in queueing an MHD error
+ * (should return #MHD_YES from #TEH_execute_refresh_link),
+ * #GNUNET_OK if we should call #reply_refresh_link_success().
+ */
+ int status;
+};
+
+
+/**
+ * Send a response for "/refresh/link".
+ *
+ * @param connection the connection to send the response to
+ * @param num_sessions number of sessions the coin was used in
+ * @param sessions array of @a num_session entries with
+ * information for each session
+ * @return a MHD result code
+ */
+static int
+reply_refresh_link_success (struct MHD_Connection *connection,
+ unsigned int num_sessions,
+ const struct TEH_RESPONSE_LinkSessionInfo *sessions)
+{
+ json_t *mlist;
+ int res;
+
+ mlist = json_array ();
+ for (unsigned int i=0;i<num_sessions;i++)
+ {
+ json_t *list = json_array ();
+ json_t *root;
+
+ for (const struct TALER_EXCHANGEDB_LinkDataList *pos = sessions[i].ldl;
+ NULL != pos;
+ pos = pos->next)
+ {
+ json_t *obj;
+
+ obj = json_object ();
+ json_object_set_new (obj,
+ "denom_pub",
+ GNUNET_JSON_from_rsa_public_key
(pos->denom_pub.rsa_public_key));
+ json_object_set_new (obj,
+ "ev_sig",
+ GNUNET_JSON_from_rsa_signature
(pos->ev_sig.rsa_signature));
+ GNUNET_assert (0 ==
+ json_array_append_new (list,
+ obj));
+ }
+ root = json_object ();
+ json_object_set_new (root,
+ "new_coins",
+ list);
+ json_object_set_new (root,
+ "transfer_pub",
+ GNUNET_JSON_from_data_auto
(&sessions[i].transfer_pub));
+ GNUNET_assert (0 ==
+ json_array_append_new (mlist,
+ root));
+ }
+ res = TEH_RESPONSE_reply_json (connection,
+ mlist,
+ MHD_HTTP_OK);
+ json_decref (mlist);
+ return res;
+}
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin. Gets the linkage data and
+ * builds the reply for the client.
+ *
+ *
+ * @param cls closure, a `struct HTD_Context`
+ * @param session_hash a session the coin was melted in
+ * @param transfer_pub public transfer key for the session
+ */
+static void
+handle_transfer_data (void *cls,
+ const struct GNUNET_HashCode *session_hash,
+ const struct TALER_TransferPublicKeyP *transfer_pub)
+{
+ struct HTD_Context *ctx = cls;
+ struct TALER_EXCHANGEDB_LinkDataList *ldl;
+ struct TEH_RESPONSE_LinkSessionInfo *lsi;
+ enum GNUNET_DB_QueryStatus qs;
+
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ctx->status)
+ return;
+ ldl = NULL;
+ qs = TEH_plugin->get_link_data_list (TEH_plugin->cls,
+ ctx->session,
+ session_hash,
+ &ldl);
+ if (qs <= 0)
+ {
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ ctx->status = GNUNET_DB_STATUS_HARD_ERROR;
+ else
+ ctx->status = qs;
+ return;
+ }
+ GNUNET_assert (NULL != ldl);
+ GNUNET_array_grow (ctx->sessions,
+ ctx->num_sessions,
+ ctx->num_sessions + 1);
+ lsi = &ctx->sessions[ctx->num_sessions - 1];
+ lsi->transfer_pub = *transfer_pub;
+ lsi->ldl = ldl;
+}
+
+
+/**
+ * Free session data kept in @a ctx
+ *
+ * @param ctx context to clean up
+ */
+static void
+purge_context (struct HTD_Context *ctx)
+{
+ for (unsigned int i=0;i<ctx->num_sessions;i++)
+ TEH_plugin->free_link_data_list (TEH_plugin->cls,
+ ctx->sessions[i].ldl);
+ GNUNET_free_non_null (ctx->sessions);
+ ctx->sessions = NULL;
+ ctx->num_sessions = 0;
+}
+
+
+/**
+ * Execute a "/refresh/link". Returns the linkage information that
+ * will allow the owner of a coin to follow the refresh trail to
+ * the refreshed coin.
+ *
+ * If it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_link_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct HTD_Context *ctx = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ ctx->session = session;
+ ctx->status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ qs = TEH_plugin->get_transfer (TEH_plugin->cls,
+ session,
+ &ctx->coin_pub,
+ &handle_transfer_data,
+ ctx);
+ ctx->session = NULL;
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
+
TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
+ "coin_pub");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (0 < qs)
+ {
+ qs = ctx->status;
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_json_pack (ctx->connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error",
+ "link data not found (link)");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return qs;
+ }
+ purge_context (ctx);
+ return qs;
+}
+
+
+/**
+ * Handle a "/refresh/link" request. Note that for "/refresh/link"
+ * we do use a simple HTTP GET, and a HTTP POST!
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ int mhd_ret;
+ int res;
+ struct HTD_Context ctx;
+
+ memset (&ctx,
+ 0,
+ sizeof (ctx));
+ res = TEH_PARSE_mhd_request_arg_data (connection,
+ "coin_pub",
+ &ctx.coin_pub,
+ sizeof (struct
TALER_CoinSpendPublicKeyP));
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &refresh_link_transaction,
+ &ctx))
+ {
+ purge_context (&ctx);
+ return mhd_ret;
+ }
+ mhd_ret = reply_refresh_link_success (connection,
+ ctx.num_sessions,
+ ctx.sessions);
+ purge_context (&ctx);
+ return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_refresh_link.c */
diff --git a/src/exchange/taler-exchange-httpd_deposit.h
b/src/exchange/taler-exchange-httpd_refresh_link.h
similarity index 63%
copy from src/exchange/taler-exchange-httpd_deposit.h
copy to src/exchange/taler-exchange-httpd_refresh_link.h
index 7bfe572..037b0d3 100644
--- a/src/exchange/taler-exchange-httpd_deposit.h
+++ b/src/exchange/taler-exchange-httpd_refresh_link.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,14 +14,14 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_deposit.h
- * @brief Handle /deposit requests
+ * @file taler-exchange-httpd_refresh_link.h
+ * @brief Handle /refresh/link requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_DEPOSIT_H
-#define TALER_EXCHANGE_HTTPD_DEPOSIT_H
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_LINK_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_LINK_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
@@ -29,9 +29,7 @@
/**
- * Handle a "/deposit" request. Parses the JSON, and, if successful,
- * checks the signatures. If everything checks out, this will
- * ultimately lead to the "/deposit" being executed, or rejected.
+ * Handle a "/refresh/link" request
*
* @param rh context of the handler
* @param connection the MHD connection to handle
@@ -41,10 +39,11 @@
* @return MHD result code
*/
int
-TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
+TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
#endif
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c
b/src/exchange/taler-exchange-httpd_refresh_melt.c
new file mode 100644
index 0000000..edcd849
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -0,0 +1,918 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 Inria & GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_melt.c
+ * @brief Handle /refresh/melt requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * @brief Details about a melt operation of an individual coin.
+ */
+struct TEH_DB_MeltDetails
+{
+
+ /**
+ * Information about the coin being melted.
+ */
+ struct TALER_CoinPublicInfo coin_info;
+
+ /**
+ * Signature allowing the melt (using
+ * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign
over.
+ */
+ struct TALER_CoinSpendSignatureP melt_sig;
+
+ /**
+ * How much of the coin's value did the client allow to be melted?
+ * This amount includes the fees, so the final amount contributed
+ * to the melt is this value minus the fee for melting the coin.
+ */
+ struct TALER_Amount melt_amount_with_fee;
+
+ /**
+ * What fee is earned by the exchange? Set delayed during
+ * #verify_coin_public_info().
+ */
+ struct TALER_Amount melt_fee;
+};
+
+
+/**
+ * Send a response for a failed "/refresh/melt" request. The
+ * transaction history of the given coin demonstrates that the
+ * @a residual value of the coin is below the @a requested
+ * contribution of the coin for the melt. Thus, the exchange
+ * refuses the melt operation.
+ *
+ * @param connection the connection to send the response to
+ * @param coin_pub public key of the coin
+ * @param coin_value original value of the coin
+ * @param tl transaction history for the coin
+ * @param requested how much this coin was supposed to contribute, including
fee
+ * @param residual remaining value of the coin (after subtracting @a tl)
+ * @return a MHD result code
+ */
+static int
+reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP
*coin_pub,
+ struct TALER_Amount coin_value,
+ struct TALER_EXCHANGEDB_TransactionList
*tl,
+ const struct TALER_Amount *requested,
+ const struct TALER_Amount *residual)
+{
+ json_t *history;
+
+ history = TEH_RESPONSE_compile_transaction_history (tl);
+ if (NULL == history)
+ return TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_FORBIDDEN,
+ "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
+ "error",
+ "insufficient funds",
+ "code",
+ (json_int_t)
TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
+ "coin_pub",
+ GNUNET_JSON_from_data_auto (coin_pub),
+ "original_value",
+ TALER_JSON_from_amount (&coin_value),
+ "residual_value",
+ TALER_JSON_from_amount (residual),
+ "requested_value",
+ TALER_JSON_from_amount (requested),
+ "history",
+ history);
+}
+
+
+/**
+ * Send a response to a "/refresh/melt" request.
+ *
+ * @param connection the connection to send the response to
+ * @param session_hash hash of the refresh session
+ * @param noreveal_index which index will the client not have to reveal
+ * @return a MHD status code
+ */
+static int
+reply_refresh_melt_success (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t noreveal_index)
+{
+ struct TALER_RefreshMeltConfirmationPS body;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+ json_t *sig_json;
+
+ body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
+ body.session_hash = *session_hash;
+ body.noreveal_index = htons (noreveal_index);
+ body.reserved = htons (0);
+ TEH_KS_sign (&body.purpose,
+ &pub,
+ &sig);
+ sig_json = GNUNET_JSON_from_data_auto (&sig);
+ GNUNET_assert (NULL != sig_json);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:i, s:o, s:o}",
+ "noreveal_index", (int) noreveal_index,
+ "exchange_sig", sig_json,
+ "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Context for the /refresh/melt operation.
+ */
+struct RefreshMeltContext
+{
+
+ /**
+ * Key state that can be used to lookup keys.
+ */
+ struct TEH_KS_StateHandle *key_state;
+
+ /**
+ * Information about the denomination key of the coin being
+ * melted.
+ */
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+
+ /**
+ * Array of denominations of the fresh coins.
+ */
+ struct TALER_DenominationPublicKey *denom_pubs;
+
+ /**
+ * Number of new coins to be generated in the melt.
+ * Size of the @e denom_pubs array.
+ */
+ unsigned int num_newcoins;
+
+ /**
+ * Details about the coin to be melted.
+ */
+ struct TEH_DB_MeltDetails coin_melt_details;
+
+ /**
+ * Set to the session hash once the @e hash_context has finished.
+ */
+ struct GNUNET_HashCode session_hash;
+
+ /**
+ * Hash operation used to calculate the session hash.
+ */
+ struct GNUNET_HashContext *hash_context;
+
+ /**
+ * Committments to the blinded envelopes for the fresh coins.
+ */
+ struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
+
+ /**
+ * Commmittments to the transfer public keys.
+ */
+ struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
+
+ /**
+ * Initialized during #refresh_melt_transaction().
+ */
+ struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+
+};
+
+
+/**
+ * Parse coin melt requests from a JSON object and write them to
+ * the database.
+ *
+ * @param connection the connection to send errors to
+ * @param session the database connection
+ * @param[in,out] rmc melt context
+ * @param[out] mhd_ret status code to return to MHD on hard error
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_check_melt (struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ struct RefreshMeltContext *rmc,
+ int *mhd_ret)
+{
+ struct TALER_EXCHANGEDB_TransactionList *tl;
+ struct TALER_EXCHANGEDB_RefreshMelt *meltp = &rmc->refresh_session.melt;
+ struct TALER_Amount coin_value;
+ struct TALER_Amount coin_residual;
+ struct TALER_Amount spent;
+ enum GNUNET_DB_QueryStatus qs;
+
+ TALER_amount_ntoh (&coin_value,
+ &rmc->dki->issue.properties.value);
+ /* fee for THIS transaction; the melt amount includes the fee! */
+ spent = rmc->coin_melt_details.melt_amount_with_fee;
+ /* add historic transaction costs of this coin */
+ qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+ session,
+
&rmc->coin_melt_details.coin_info.coin_pub,
+ &tl);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
+ return qs;
+ }
+ if (GNUNET_OK !=
+ TEH_DB_calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
+ {
+ GNUNET_break (0);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* Refuse to refresh when the coin's value is insufficient
+ for the cost of all transactions. */
+ if (TALER_amount_cmp (&coin_value,
+ &spent) < 0)
+ {
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&coin_residual,
+ &spent,
+
&rmc->coin_melt_details.melt_amount_with_fee));
+ *mhd_ret = reply_refresh_melt_insufficient_funds (connection,
+
&rmc->coin_melt_details.coin_info.coin_pub,
+ coin_value,
+ tl,
+
&rmc->coin_melt_details.melt_amount_with_fee,
+ &coin_residual);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+
+ meltp->coin = rmc->coin_melt_details.coin_info;
+ meltp->coin_sig = rmc->coin_melt_details.melt_sig;
+ meltp->session_hash = rmc->session_hash;
+ meltp->amount_with_fee = rmc->coin_melt_details.melt_amount_with_fee;
+ meltp->melt_fee = rmc->coin_melt_details.melt_fee;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Execute a "/refresh/melt". We have been given a list of valid
+ * coins and a request to melt them into the given @a
+ * refresh_session_pub. Check that the coins all have the required
+ * value left and if so, store that they have been melted and confirm
+ * the melting operation to the client.
+ *
+ * If it returns a non-error code, the transaction logic MUST NOT
+ * queue a MHD response. IF it returns an hard error, the transaction
+ * logic MUST queue a MHD response and set @a mhd_ret. If it returns
+ * the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls our `struct RefreshMeltContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_melt_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct RefreshMeltContext *rmc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
+ session,
+ &rmc->session_hash,
+ &rmc->refresh_session);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ *mhd_ret = reply_refresh_melt_success (connection,
+ &rmc->session_hash,
+ rmc->refresh_session.noreveal_index);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
+ return qs;
+ }
+
+ /* store 'global' session data */
+ rmc->refresh_session.num_newcoins = rmc->num_newcoins;
+ rmc->refresh_session.noreveal_index
+ = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+ TALER_CNC_KAPPA);
+
+ qs = refresh_check_melt (connection,
+ session,
+ rmc,
+ mhd_ret);
+ if (0 > qs)
+ return qs;
+
+ if ( (0 >=
+ (qs = TEH_plugin->create_refresh_session (TEH_plugin->cls,
+ session,
+ &rmc->session_hash,
+ &rmc->refresh_session))) ||
+ (0 >=
+ (qs = TEH_plugin->insert_refresh_order (TEH_plugin->cls,
+ session,
+ &rmc->session_hash,
+ rmc->num_newcoins,
+ rmc->denom_pubs))) ||
+ (0 >=
+ (qs = TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
+ session,
+ &rmc->session_hash,
+ rmc->num_newcoins,
+
rmc->commit_coin[rmc->refresh_session.noreveal_index]))) ||
+ (0 >=
+ (qs = TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
+ session,
+
&rmc->session_hash,
+
&rmc->transfer_pub[rmc->refresh_session.noreveal_index]))) )
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return qs;
+ }
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request after the main JSON parsing has
+ * happened. We now need to validate the coins being melted and the
+ * session signature and then hand things of to execute the melt
+ * operation.
+ *
+ * @param connection the MHD connection to handle
+ * @param[out] mhd_ret set on failure to return value for MHD
+ * @param rmc information about the melt to process
+ * @return MHD result code
+ */
+static int
+refresh_melt_prepare (struct MHD_Connection *connection,
+ int *mhd_ret,
+ struct RefreshMeltContext *rmc)
+{
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
+ struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+ struct TALER_Amount cost;
+ struct TALER_Amount total_cost;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_melt;
+ struct TALER_Amount total_melt;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "/refresh/melt request for session %s\n",
+ GNUNET_h2s (&rmc->session_hash));
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (TEH_exchange_currency_string,
+ &total_cost));
+ for (unsigned int i=0;i<rmc->num_newcoins;i++)
+ {
+ dk = TEH_KS_denomination_key_lookup (rmc->key_state,
+ &rmc->denom_pubs[i],
+ TEH_KS_DKU_WITHDRAW);
+ if (NULL == dk)
+ {
+ GNUNET_break_op (0);
+ *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+
TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND,
+ "new_denoms");
+ return GNUNET_SYSERR;
+ }
+ dki = &dk->issue;
+ TALER_amount_ntoh (&value,
+ &dki->properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &dki->properties.fee_withdraw);
+ if ( (GNUNET_OK !=
+ TALER_amount_add (&cost,
+ &value,
+ &fee_withdraw)) ||
+ (GNUNET_OK !=
+ TALER_amount_add (&total_cost,
+ &cost,
+ &total_cost)) )
+ {
+ GNUNET_break_op (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW,
+ "cost calculation failure");
+ return GNUNET_SYSERR;
+ }
+ }
+
+ dki = &rmc->dki->issue;
+ TALER_amount_ntoh (&fee_melt,
+ &dki->properties.fee_refresh);
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&total_melt,
+ &rmc->coin_melt_details.melt_amount_with_fee,
+ &fee_melt))
+ {
+ GNUNET_break_op (0);
+ *mhd_ret = TEH_RESPONSE_reply_external_error (connection,
+
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
+ "Melt contribution below
melting fee");
+ return GNUNET_SYSERR;
+ }
+ if (0 !=
+ TALER_amount_cmp (&total_cost,
+ &total_melt))
+ {
+ GNUNET_break_op (0);
+ /* We require total value of coins being melted and
+ total value of coins being generated to match! */
+ *mhd_ret = TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:I}",
+ "error", "value mismatch",
+ "code", (json_int_t)
TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
+ return GNUNET_SYSERR;
+ }
+ return TEH_DB_run_transaction (connection,
+ mhd_ret,
+ &refresh_melt_transaction,
+ rmc);
+}
+
+
+/**
+ * Extract public coin information from a JSON object.
+ *
+ * @param connection the connection to send error responses to
+ * @param coin_info the JSON object to extract the coin info from
+ * @param[out] r_melt_detail set to details about the coin's melting
permission (if valid)
+ * @return #GNUNET_YES if coin public info in JSON was valid
+ * #GNUNET_NO JSON was invalid, response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+get_coin_public_info (struct MHD_Connection *connection,
+ const json_t *coin_info,
+ struct TEH_DB_MeltDetails *r_melt_detail)
+{
+ int ret;
+ struct TALER_CoinSpendSignatureP melt_sig;
+ struct TALER_DenominationSignature sig;
+ struct TALER_DenominationPublicKey pk;
+ struct TALER_Amount amount;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("coin_pub",
&r_melt_detail->coin_info.coin_pub),
+ TALER_JSON_spec_denomination_signature ("denom_sig", &sig),
+ TALER_JSON_spec_denomination_public_key ("denom_pub", &pk),
+ GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig),
+ TALER_JSON_spec_amount ("value_with_fee", &amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ret = TEH_PARSE_json_data (connection,
+ coin_info,
+ spec);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_break_op (0);
+ return ret;
+ }
+ /* check exchange signature on the coin */
+ r_melt_detail->coin_info.denom_sig = sig;
+ r_melt_detail->coin_info.denom_pub = pk;
+ if (GNUNET_OK !=
+ TALER_test_coin_valid (&r_melt_detail->coin_info))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ r_melt_detail->coin_info.denom_sig.rsa_signature = NULL;
+ r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL;
+ return (MHD_YES ==
+ TEH_RESPONSE_reply_signature_invalid (connection,
+
TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
+ "denom_sig"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ r_melt_detail->melt_sig = melt_sig;
+ r_melt_detail->melt_amount_with_fee = amount;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Release memory from the @a commit_coin array.
+ *
+ * @param commit_coin array to release
+ * @param kappa size of 1st dimension
+ * @param num_new_coins size of 2nd dimension
+ */
+static void
+free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
+ unsigned int kappa,
+ unsigned int num_new_coins)
+{
+ for (unsigned int i=0;i<kappa;i++)
+ {
+ if (NULL == commit_coin[i])
+ break;
+ for (unsigned int j=0;j<num_new_coins;j++)
+ GNUNET_free_non_null (commit_coin[i][j].coin_ev);
+ GNUNET_free (commit_coin[i]);
+ commit_coin[i] = NULL;
+ }
+}
+
+
+/**
+ * Cleanup state kept in the @a rmc.
+ *
+ * @param rmc state to clean up; does not free @a rmc itself
+ */
+static void
+cleanup_rmc (struct RefreshMeltContext *rmc)
+{
+ free_commit_coins (rmc->commit_coin,
+ TALER_CNC_KAPPA,
+ rmc->num_newcoins);
+ if (NULL != rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free
(rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key);
+ rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key = NULL;
+ }
+ if (NULL != rmc->coin_melt_details.coin_info.denom_sig.rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free
(rmc->coin_melt_details.coin_info.denom_sig.rsa_signature);
+ rmc->coin_melt_details.coin_info.denom_sig.rsa_signature = NULL;
+ }
+ if (NULL != rmc->denom_pubs)
+ {
+ for (unsigned int j=0;j<rmc->num_newcoins;j++)
+ if (NULL != rmc->denom_pubs[j].rsa_public_key)
+ GNUNET_CRYPTO_rsa_public_key_free (rmc->denom_pubs[j].rsa_public_key);
+ GNUNET_free (rmc->denom_pubs);
+ rmc->denom_pubs = NULL;
+ }
+ if (NULL != rmc->hash_context)
+ {
+ GNUNET_CRYPTO_hash_context_abort (rmc->hash_context);
+ rmc->hash_context = NULL;
+ }
+ if (NULL != rmc->key_state)
+ {
+ TEH_KS_release (rmc->key_state);
+ rmc->key_state = NULL;
+ }
+}
+
+
+/**
+ * Handle a "/refresh/melt" request after the first parsing has
+ * happened. We now need to validate the coins being melted and the
+ * session signature and then hand things of to execute the melt
+ * operation. This function parses the JSON arrays and then passes
+ * processing on to #handle_refresh_melt_binary().
+ *
+ * @param connection the MHD connection to handle
+ * @param new_denoms array of denomination keys
+ * @param melt_coin coin to melt
+ * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of transfer keys
+ * @param coin_evs #TALER_CNC_KAPPA-dimensional array of envelopes to sign
+ * @return MHD result code
+ */
+static int
+handle_refresh_melt_json (struct MHD_Connection *connection,
+ const json_t *new_denoms,
+ const json_t *melt_coin,
+ const json_t *transfer_pubs,
+ const json_t *coin_evs)
+{
+ int res;
+ int mhd_ret;
+ struct RefreshMeltContext rmc;
+
+ memset (&rmc,
+ 0,
+ sizeof (rmc));
+ /* For the signature check, we hash most of the inputs together
+ (except for the signatures on the coins). */
+ rmc.hash_context = GNUNET_CRYPTO_hash_context_start ();
+ for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
+ {
+ struct GNUNET_JSON_Specification trans_spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, &rmc.transfer_pub[i]),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_json_array (connection,
+ transfer_pubs,
+ trans_spec,
+ i, -1);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ cleanup_rmc (&rmc);
+ return mhd_ret;
+ }
+ GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+ &rmc.transfer_pub[i],
+ sizeof (struct TALER_TransferPublicKeyP));
+ }
+
+ rmc.num_newcoins = json_array_size (new_denoms);
+ rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins,
+ struct TALER_DenominationPublicKey);
+ for (unsigned int i=0;i<rmc.num_newcoins;i++)
+ {
+ char *buf;
+ size_t buf_size;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_denomination_public_key (NULL,
+ &rmc.denom_pubs[i]),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_json_array (connection,
+ new_denoms,
+ spec,
+ i,
+ -1);
+ if (GNUNET_OK != res)
+ {
+ mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ cleanup_rmc (&rmc);
+ return mhd_ret;
+ }
+ buf_size = GNUNET_CRYPTO_rsa_public_key_encode
(rmc.denom_pubs[i].rsa_public_key,
+ &buf);
+ GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+ buf,
+ buf_size);
+ GNUNET_free (buf);
+ }
+
+ /* decode JSON data on coin to melt and check that this is a
+ valid coin */
+ {
+ struct TALER_AmountNBO melt_amount;
+
+ res = get_coin_public_info (connection,
+ melt_coin,
+ &rmc.coin_melt_details);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ cleanup_rmc (&rmc);
+ return mhd_ret;
+ }
+ TALER_amount_hton (&melt_amount,
+ &rmc.coin_melt_details.melt_amount_with_fee);
+ GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+ &rmc.coin_melt_details.coin_info.coin_pub,
+ sizeof (struct
TALER_CoinSpendPublicKeyP));
+ GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+ &melt_amount,
+ sizeof (struct TALER_AmountNBO));
+ }
+
+ /* parse JSON arrays into binary arrays and hash everything
+ together for the signature check */
+ for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
+ {
+ rmc.commit_coin[i] = GNUNET_new_array (rmc.num_newcoins,
+ struct
TALER_EXCHANGEDB_RefreshCommitCoin);
+ for (unsigned int j = 0; j < rmc.num_newcoins; j++)
+ {
+ struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &rmc.commit_coin[i][j];
+ struct GNUNET_JSON_Specification coin_spec[] = {
+ GNUNET_JSON_spec_varsize (NULL,
+ (void **) &rcc->coin_ev,
+ &rcc->coin_ev_size),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_json_array (connection,
+ coin_evs,
+ coin_spec,
+ i,
+ j,
+ -1);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ cleanup_rmc (&rmc);
+ return mhd_ret;
+ }
+
+ GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+ rcc->coin_ev,
+ rcc->coin_ev_size);
+ }
+ }
+
+ GNUNET_CRYPTO_hash_context_finish (rmc.hash_context,
+ &rmc.session_hash);
+ rmc.hash_context = NULL;
+
+ rmc.key_state = TEH_KS_acquire ();
+ rmc.dki = TEH_KS_denomination_key_lookup (rmc.key_state,
+
&rmc.coin_melt_details.coin_info.denom_pub,
+ TEH_KS_DKU_DEPOSIT);
+ if (NULL == rmc.dki)
+ {
+ TEH_KS_release (rmc.key_state);
+ TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
+ return TEH_RESPONSE_reply_arg_unknown (connection,
+
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
+ "denom_pub");
+ }
+
+ /* verify signature of coin for melt operation */
+ {
+ struct TALER_RefreshMeltCoinAffirmationPS body;
+ struct TALER_Amount fee_refresh;
+
+ TALER_amount_ntoh (&fee_refresh,
+ &rmc.dki->issue.properties.fee_refresh);
+ rmc.coin_melt_details.melt_fee = fee_refresh;
+ body.purpose.size = htonl (sizeof (struct
TALER_RefreshMeltCoinAffirmationPS));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+ body.session_hash = rmc.session_hash;
+ TALER_amount_hton (&body.amount_with_fee,
+ &rmc.coin_melt_details.melt_amount_with_fee);
+ TALER_amount_hton (&body.melt_fee,
+ &fee_refresh);
+ body.coin_pub = rmc.coin_melt_details.coin_info.coin_pub;
+ if (TALER_amount_cmp (&fee_refresh,
+ &rmc.coin_melt_details.melt_amount_with_fee) > 0)
+ {
+ GNUNET_break_op (0);
+ cleanup_rmc (&rmc);
+ return TEH_RESPONSE_reply_external_error (connection,
+
TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
+ "melt amount smaller than
melting fee");
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
+ &body.purpose,
+
&rmc.coin_melt_details.melt_sig.eddsa_signature,
+
&rmc.coin_melt_details.coin_info.coin_pub.eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ cleanup_rmc (&rmc);
+ return TEH_RESPONSE_reply_signature_invalid (connection,
+
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
+ "confirm_sig");
+ }
+ }
+
+ /* prepare commit */
+ if (GNUNET_OK !=
+ refresh_melt_prepare (connection,
+ &mhd_ret,
+ &rmc))
+ {
+ cleanup_rmc (&rmc);
+ return mhd_ret;
+ }
+ mhd_ret = reply_refresh_melt_success (connection,
+ &rmc.session_hash,
+ rmc.refresh_session.noreveal_index);
+ cleanup_rmc (&rmc);
+ return mhd_ret;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request. Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TEH_DB_execute_refresh_melt().
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *root;
+ json_t *new_denoms;
+ json_t *melt_coin;
+ json_t *coin_evs;
+ json_t *transfer_pubs;
+ int res;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("new_denoms", &new_denoms),
+ GNUNET_JSON_spec_json ("melt_coin", &melt_coin),
+ GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
+ GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) ||
+ (NULL == root) )
+ return MHD_YES;
+
+ res = TEH_PARSE_json_data (connection,
+ root,
+ spec);
+ json_decref (root);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+
+ /* Determine dimensionality of the request (kappa, #old and #new coins) */
+ if (TALER_CNC_KAPPA != json_array_size (coin_evs))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+
TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID,
+ "coin_evs");
+ }
+ if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+
TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID,
+ "transfer_pubs");
+ }
+ res = handle_refresh_melt_json (connection,
+ new_denoms,
+ melt_coin,
+ transfer_pubs,
+ coin_evs);
+ GNUNET_JSON_parse_free (spec);
+ return res;
+}
+
+
+/* end of taler-exchange-httpd_refresh_melt.c */
diff --git a/src/exchange/taler-exchange-httpd_refund.h
b/src/exchange/taler-exchange-httpd_refresh_melt.h
similarity index 60%
copy from src/exchange/taler-exchange-httpd_refund.h
copy to src/exchange/taler-exchange-httpd_refresh_melt.h
index 1fa52b1..a938abf 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,14 +14,14 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_refund.h
- * @brief Handle /refund requests
+ * @file taler-exchange-httpd_refresh_melt.h
+ * @brief Handle /refresh/melt requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_REFUND_H
-#define TALER_EXCHANGE_HTTPD_REFUND_H
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_MELT_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_MELT_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
@@ -29,11 +29,10 @@
/**
- * Handle a "/refund" request. Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_refund() to
- * further check the details of the operation specified. If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/refresh/melt" request. Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TEH_DB_execute_refresh_melt().
*
* @param rh context of the handler
* @param connection the MHD connection to handle
@@ -41,12 +40,13 @@
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
- */
+ */
int
-TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
+TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
#endif
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c
b/src/exchange/taler-exchange-httpd_refresh_reveal.c
new file mode 100644
index 0000000..cfb2b68
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -0,0 +1,769 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 Inria & GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_reveal.c
+ * @brief Handle /refresh/reveal requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Send a response for "/refresh/reveal".
+ *
+ * @param connection the connection to send the response to
+ * @param num_newcoins number of new coins for which we reveal data
+ * @param sigs array of @a num_newcoins signatures revealed
+ * @return a MHD result code
+ */
+static int
+reply_refresh_reveal_success (struct MHD_Connection *connection,
+ unsigned int num_newcoins,
+ const struct TALER_DenominationSignature *sigs)
+{
+ json_t *root;
+ json_t *obj;
+ json_t *list;
+ int ret;
+
+ list = json_array ();
+ for (unsigned int newcoin_index = 0;
+ newcoin_index < num_newcoins;
+ newcoin_index++)
+ {
+ obj = json_object ();
+ json_object_set_new (obj,
+ "ev_sig",
+ GNUNET_JSON_from_rsa_signature
(sigs[newcoin_index].rsa_signature));
+ GNUNET_assert (0 ==
+ json_array_append_new (list,
+ obj));
+ }
+ root = json_object ();
+ json_object_set_new (root,
+ "ev_sigs",
+ list);
+ ret = TEH_RESPONSE_reply_json (connection,
+ root,
+ MHD_HTTP_OK);
+ json_decref (root);
+ return ret;
+}
+
+
+/**
+ * Send a response for a failed "/refresh/reveal", where the
+ * revealed value(s) do not match the original commitment.
+ *
+ * @param connection the connection to send the response to
+ * @param session info about session
+ * @param commit_coins array of @a num_newcoins committed envelopes at offset
@a gamma
+ * @param denom_pubs array of @a num_newcoins denomination keys for the new
coins
+ * @param gamma_tp transfer public key at offset @a gamma
+ * @return a MHD result code
+ */
+static int
+reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_RefreshSession
*session,
+ const struct TALER_EXCHANGEDB_RefreshCommitCoin
*commit_coins,
+ const struct TALER_DenominationPublicKey
*denom_pubs,
+ const struct TALER_TransferPublicKeyP *gamma_tp)
+{
+ json_t *info_new;
+ json_t *info_commit_k;
+
+ info_new = json_array ();
+ info_commit_k = json_array ();
+ for (unsigned int i=0;i<session->num_newcoins;i++)
+ {
+ const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
+ json_t *cc_json;
+
+ GNUNET_assert (0 ==
+ json_array_append_new (info_new,
+ GNUNET_JSON_from_rsa_public_key
(denom_pubs[i].rsa_public_key)));
+
+ cc = &commit_coins[i];
+ cc_json = json_pack ("{s:o}",
+ "coin_ev",
+ GNUNET_JSON_from_data (cc->coin_ev,
+ cc->coin_ev_size));
+ GNUNET_assert (0 ==
+ json_array_append_new (info_commit_k,
+ cc_json));
+ }
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_CONFLICT,
+ "{s:s, s:I, s:o, s:o, s:o, s:o, s:o,
s:o, s:o, s:i}",
+ "error", "commitment violation",
+ "code", (json_int_t)
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
+ "coin_sig", GNUNET_JSON_from_data_auto
(&session->melt.coin_sig),
+ "coin_pub", GNUNET_JSON_from_data_auto
(&session->melt.coin.coin_pub),
+ "melt_amount_with_fee",
TALER_JSON_from_amount (&session->melt.amount_with_fee),
+ "melt_fee", TALER_JSON_from_amount
(&session->melt.melt_fee),
+ "newcoin_infos", info_new,
+ "commit_infos", info_commit_k,
+ "gamma_tp", GNUNET_JSON_from_data_auto
(gamma_tp),
+ "gamma", (int) session->noreveal_index);
+}
+
+
+/**
+ * Check if the given @a transfer_privs correspond to an honest
+ * commitment for the given session.
+ * Checks that the transfer private keys match their commitments.
+ * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that
they match.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param off commitment offset to check
+ * @param transfer_priv private transfer key
+ * @param melt information about the melted coin
+ * @param num_newcoins number of newcoins being generated
+ * @param denom_pubs array of @a num_newcoins keys for the new coins
+ * @param hash_context hash context to update by hashing in the data
+ * from this offset
+ * @return #GNUNET_OK if the committment was honest,
+ * #GNUNET_NO if there was a problem and we generated an error message
+ * #GNUNET_SYSERR if we could not even generate an error message
+ */
+static int
+check_commitment (struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int off,
+ const struct TALER_TransferPrivateKeyP *transfer_priv,
+ const struct TALER_EXCHANGEDB_RefreshMelt *melt,
+ unsigned int num_newcoins,
+ const struct TALER_DenominationPublicKey *denom_pubs,
+ struct GNUNET_HashContext *hash_context)
+{
+ struct TALER_TransferSecretP transfer_secret;
+
+ TALER_link_reveal_transfer_secret (transfer_priv,
+ &melt->coin.coin_pub,
+ &transfer_secret);
+
+ /* Check that the commitments for all new coins were correct */
+ for (unsigned int j = 0; j < num_newcoins; j++)
+ {
+ struct TALER_FreshCoinP fc;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct GNUNET_HashCode h_msg;
+ char *buf;
+ size_t buf_len;
+
+ TALER_setup_fresh_coin (&transfer_secret,
+ j,
+ &fc);
+ GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ GNUNET_CRYPTO_hash (&coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP),
+ &h_msg);
+ if (GNUNET_YES !=
+ GNUNET_CRYPTO_rsa_blind (&h_msg,
+ &fc.blinding_key.bks,
+ denom_pubs[j].rsa_public_key,
+ &buf,
+ &buf_len))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Blind failed (bad denomination key!?)\n");
+ return (MHD_YES ==
+ TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
+ "Blinding error"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ buf,
+ buf_len);
+ GNUNET_free (buf);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * State for a /refresh/reveal operation.
+ */
+struct RevealContext
+{
+
+ /**
+ * Hash of the refresh session.
+ */
+ const struct GNUNET_HashCode *session_hash;
+
+ /**
+ * Database session used to execute the transaction.
+ */
+ struct TALER_EXCHANGEDB_Session *session;
+
+ /**
+ * Session state from the database.
+ */
+ struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+
+ /**
+ * Array of denomination public keys used for the refresh.
+ */
+ struct TALER_DenominationPublicKey *denom_pubs;
+
+ /**
+ * Envelopes with the signatures to be returned.
+ */
+ struct TALER_DenominationSignature *ev_sigs;
+
+ /**
+ * Commitment data from the DB giving data about original
+ * commitments, in particular the blinded envelopes (for
+ * index gamma).
+ */
+ struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
+
+ /**
+ * Transfer public key associated with the gamma value
+ * selected by the exchange.
+ */
+ struct TALER_TransferPublicKeyP gamma_tp;
+
+ /**
+ * Transfer private keys revealed to us.
+ */
+ struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
+
+};
+
+
+/**
+ * Exchange a coin as part of a refresh operation. Obtains the
+ * envelope from the database and performs the signing operation.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param key_state key state to lookup denomination pubs
+ * @param denom_pub denomination key for the coin to create
+ * @param commit_coin the coin that was committed
+ * @param coin_off number of the coin
+ * @param[out] ev_sig set to signature over the coin upon success
+ * @return database transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_exchange_coin (struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ struct TEH_KS_StateHandle *key_state,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ const struct TALER_EXCHANGEDB_RefreshCommitCoin
*commit_coin,
+ unsigned int coin_off,
+ struct TALER_DenominationSignature *ev_sig)
+{
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+ enum GNUNET_DB_QueryStatus qs;
+
+ dki = TEH_KS_denomination_key_lookup (key_state,
+ denom_pub,
+ TEH_KS_DKU_WITHDRAW);
+ if (NULL == dki)
+ {
+ GNUNET_break (0);
+ ev_sig->rsa_signature = NULL;
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ qs = TEH_plugin->get_refresh_out (TEH_plugin->cls,
+ session,
+ session_hash,
+ coin_off,
+ ev_sig);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning cached reply for /refresh/reveal signature\n");
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
+ return qs;
+
+ ev_sig->rsa_signature
+ = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
+ commit_coin->coin_ev,
+ commit_coin->coin_ev_size);
+ if (NULL == ev_sig->rsa_signature)
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ qs = TEH_plugin->insert_refresh_out (TEH_plugin->cls,
+ session,
+ session_hash,
+ coin_off,
+ ev_sig);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (NULL != ev_sig->rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (ev_sig->rsa_signature);
+ ev_sig->rsa_signature = NULL;
+ }
+ }
+ return qs;
+}
+
+
+/**
+ * Cleanup state of the transaction stored in @a rc.
+ *
+ * @param rc context to clean up
+ */
+static void
+cleanup_rc (struct RevealContext *rc)
+{
+ if (NULL != rc->denom_pubs)
+ {
+ for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++)
+ if (NULL != rc->denom_pubs[i].rsa_public_key)
+ GNUNET_CRYPTO_rsa_public_key_free (rc->denom_pubs[i].rsa_public_key);
+ GNUNET_free (rc->denom_pubs);
+ rc->denom_pubs = NULL;
+ }
+ if (NULL != rc->commit_coins)
+ {
+ for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+ GNUNET_free_non_null (rc->commit_coins[j].coin_ev);
+ GNUNET_free (rc->commit_coins);
+ rc->commit_coins = NULL;
+ }
+ if (NULL != rc->ev_sigs)
+ {
+ for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+ if (NULL != rc->ev_sigs[j].rsa_signature)
+ GNUNET_CRYPTO_rsa_signature_free (rc->ev_sigs[j].rsa_signature);
+ GNUNET_free (rc->ev_sigs);
+ rc->ev_sigs = NULL;
+ }
+ if (NULL != rc->refresh_session.melt.coin.denom_sig.rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free
(rc->refresh_session.melt.coin.denom_sig.rsa_signature);
+ rc->refresh_session.melt.coin.denom_sig.rsa_signature = NULL;
+ }
+ if (NULL != rc->refresh_session.melt.coin.denom_pub.rsa_public_key)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free
(rc->refresh_session.melt.coin.denom_pub.rsa_public_key);
+ rc->refresh_session.melt.coin.denom_pub.rsa_public_key = NULL;
+ }
+}
+
+
+/**
+ * Execute a "/refresh/reveal". The client is revealing to us the
+ * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the
+ * revealed transfer keys would allow linkage to the blinded coins,
+ * and if so, return the signed coins for corresponding to the set of
+ * coins that was not chosen.
+ *
+ * IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure of type `struct RevealContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_reveal_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct RevealContext *rc = cls;
+ unsigned int off;
+ struct GNUNET_HashContext *hash_context;
+ struct GNUNET_HashCode sh_check;
+ enum GNUNET_DB_QueryStatus qs;
+
+ rc->session = session;
+ qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
+ session,
+ rc->session_hash,
+ &rc->refresh_session);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
+ "session_hash");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
+ (rc->refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
+ {
+ GNUNET_break (0);
+ cleanup_rc (rc);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ rc->denom_pubs = GNUNET_new_array (rc->refresh_session.num_newcoins,
+ struct TALER_DenominationPublicKey);
+ qs = TEH_plugin->get_refresh_order (TEH_plugin->cls,
+ session,
+ rc->session_hash,
+ rc->refresh_session.num_newcoins,
+ rc->denom_pubs);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ cleanup_rc (rc);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
+ /* first, iterate over transfer public keys for hash_context */
+ off = 0;
+ for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+ {
+ if (i == rc->refresh_session.noreveal_index)
+ {
+ off = 1;
+ /* obtain gamma_tp from db */
+ qs = TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
+ session,
+ rc->session_hash,
+ &rc->gamma_tp);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ cleanup_rc (rc);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &rc->gamma_tp,
+ sizeof (struct
TALER_TransferPublicKeyP));
+ }
+ else
+ {
+ /* compute tp from private key */
+ struct TALER_TransferPublicKeyP tp;
+
+ GNUNET_CRYPTO_ecdhe_key_get_public (&rc->transfer_privs[i -
off].ecdhe_priv,
+ &tp.ecdhe_pub);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &tp,
+ sizeof (struct
TALER_TransferPublicKeyP));
+ }
+ }
+
+ /* next, add all of the hashes from the denomination keys to the
+ hash_context */
+ for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++)
+ {
+ char *buf;
+ size_t buf_size;
+
+ buf_size = GNUNET_CRYPTO_rsa_public_key_encode
(rc->denom_pubs[i].rsa_public_key,
+ &buf);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ buf,
+ buf_size);
+ GNUNET_free (buf);
+ }
+
+ /* next, add public key of coin and amount being refreshed */
+ {
+ struct TALER_AmountNBO melt_amountn;
+
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &rc->refresh_session.melt.coin.coin_pub,
+ sizeof (struct
TALER_CoinSpendPublicKeyP));
+ TALER_amount_hton (&melt_amountn,
+ &rc->refresh_session.melt.amount_with_fee);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &melt_amountn,
+ sizeof (struct TALER_AmountNBO));
+ }
+
+ rc->commit_coins = GNUNET_new_array (rc->refresh_session.num_newcoins,
+ struct
TALER_EXCHANGEDB_RefreshCommitCoin);
+ off = 0;
+ for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+ {
+ int res;
+
+ if (i == rc->refresh_session.noreveal_index)
+ {
+ off = 1;
+ /* obtain commit_coins for the selected gamma value from DB */
+ qs = TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
+ session,
+ rc->session_hash,
+
rc->refresh_session.num_newcoins,
+ rc->commit_coins);
+ if (0 >= qs)
+ {
+ cleanup_rc (rc);
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* add envelopes to hash_context */
+ for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+ {
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ rc->commit_coins[j].coin_ev,
+ rc->commit_coins[j].coin_ev_size);
+ }
+ continue;
+ }
+ if (GNUNET_OK !=
+ (res = check_commitment (connection,
+ session,
+ rc->session_hash,
+ i,
+ &rc->transfer_privs[i - off],
+ &rc->refresh_session.melt,
+ rc->refresh_session.num_newcoins,
+ rc->denom_pubs,
+ hash_context)))
+ {
+ GNUNET_break_op (0);
+ cleanup_rc (rc);
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ *mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+
+ /* Check session hash matches */
+ GNUNET_CRYPTO_hash_context_finish (hash_context,
+ &sh_check);
+ if (0 != memcmp (&sh_check,
+ rc->session_hash,
+ sizeof (struct GNUNET_HashCode)))
+ {
+ GNUNET_break_op (0);
+ *mhd_ret = reply_refresh_reveal_missmatch (connection,
+ &rc->refresh_session,
+ rc->commit_coins,
+ rc->denom_pubs,
+ &rc->gamma_tp);
+ cleanup_rc (rc);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* Client request OK, sign coins */
+ rc->ev_sigs = GNUNET_new_array (rc->refresh_session.num_newcoins,
+ struct TALER_DenominationSignature);
+ {
+ struct TEH_KS_StateHandle *key_state;
+
+ key_state = TEH_KS_acquire ();
+ for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+ {
+ qs = refresh_exchange_coin (connection,
+ session,
+ rc->session_hash,
+ key_state,
+ &rc->denom_pubs[j],
+ &rc->commit_coins[j],
+ j,
+ &rc->ev_sigs[j]);
+ if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ||
+ (NULL == rc->ev_sigs[j].rsa_signature) )
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ break;
+ }
+ }
+ TEH_KS_release (key_state);
+ }
+ if (0 >= qs)
+ {
+ cleanup_rc (rc);
+ return qs;
+ }
+ return qs;
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request. Parses the given JSON
+ * transfer private keys and if successful, passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash identifying the melting session
+ * @param tp_json private transfer keys in JSON format
+ * @return MHD result code
+ */
+static int
+handle_refresh_reveal_json (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ const json_t *tp_json)
+{
+ struct RevealContext rc;
+ int mhd_ret;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "reveal request for session %s\n",
+ GNUNET_h2s (session_hash));
+ memset (&rc,
+ 0,
+ sizeof (rc));
+ rc.session_hash = session_hash;
+ for (unsigned int i = 0; i < TALER_CNC_KAPPA - 1; i++)
+ {
+ struct GNUNET_JSON_Specification tp_spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, &rc.transfer_privs[i]),
+ GNUNET_JSON_spec_end ()
+ };
+ int res;
+
+ res = TEH_PARSE_json_array (connection,
+ tp_json,
+ tp_spec,
+ i,
+ -1);
+ GNUNET_break_op (GNUNET_OK == res);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &refresh_reveal_transaction,
+ &rc))
+ {
+ cleanup_rc (&rc);
+ return mhd_ret;
+ }
+ mhd_ret = reply_refresh_reveal_success (connection,
+ rc.refresh_session.num_newcoins,
+ rc.ev_sigs);
+ cleanup_rc (&rc);
+ return mhd_ret;
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request. This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/melt". This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_HashCode session_hash;
+ int res;
+ json_t *root;
+ json_t *transfer_privs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
+ GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) ||
+ (NULL == root) )
+ return MHD_YES;
+
+ res = TEH_PARSE_json_data (connection,
+ root,
+ spec);
+ json_decref (root);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ /* Determine dimensionality of the request (kappa and #old coins) */
+ /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
+ if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
+ {
+ GNUNET_JSON_parse_free (spec);
+ GNUNET_break_op (0);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
+ "transfer_privs");
+ }
+ res = handle_refresh_reveal_json (connection,
+ &session_hash,
+ transfer_privs);
+ GNUNET_JSON_parse_free (spec);
+ return res;
+}
+
+
+/* end of taler-exchange-httpd_refresh_reveal.c */
diff --git a/src/exchange/taler-exchange-httpd_refund.h
b/src/exchange/taler-exchange-httpd_refresh_reveal.h
similarity index 55%
copy from src/exchange/taler-exchange-httpd_refund.h
copy to src/exchange/taler-exchange-httpd_refresh_reveal.h
index 1fa52b1..cc5ac3f 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,14 +14,14 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_refund.h
- * @brief Handle /refund requests
+ * @file taler-exchange-httpd_refresh_reveal.h
+ * @brief Handle /refresh/reveal requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_REFUND_H
-#define TALER_EXCHANGE_HTTPD_REFUND_H
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
@@ -29,11 +29,13 @@
/**
- * Handle a "/refund" request. Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_refund() to
- * further check the details of the operation specified. If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/refresh/reveal" request. This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/commit". This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
@@ -43,10 +45,10 @@
* @return MHD result code
*/
int
-TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
+TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
#endif
diff --git a/src/exchange/taler-exchange-httpd_refund.c
b/src/exchange/taler-exchange-httpd_refund.c
index a5787ee..9846c73 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+ Copyright (C) 2014-2017 Inria and GNUnet e.V.
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
@@ -37,6 +37,350 @@
/**
+ * Generate successful refund confirmation message.
+ *
+ * @param connection connection to the client
+ * @param refund details about the successful refund
+ * @return MHD result code
+ */
+static int
+reply_refund_success (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_Refund *refund)
+{
+ struct TALER_RefundConfirmationPS rc;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+
+ rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
+ rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
+ rc.h_contract_terms = refund->h_contract_terms;
+ rc.coin_pub = refund->coin.coin_pub;
+ rc.merchant = refund->merchant_pub;
+ rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
+ TALER_amount_hton (&rc.refund_amount,
+ &refund->refund_amount);
+ TALER_amount_hton (&rc.refund_fee,
+ &refund->refund_fee);
+ TEH_KS_sign (&rc.purpose,
+ &pub,
+ &sig);
+ return TEH_RESPONSE_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));
+}
+
+
+/**
+ * Generate generic refund failure message. All the details
+ * are in the @a response_code. The body can be empty.
+ *
+ * @param connection connection to the client
+ * @param response_code response code to generate
+ * @param ec taler error code to include
+ * @return MHD result code
+ */
+static int
+reply_refund_failure (struct MHD_Connection *connection,
+ unsigned int response_code,
+ enum TALER_ErrorCode ec)
+{
+ return TEH_RESPONSE_reply_json_pack (connection,
+ response_code,
+ "{s:s, s:I}",
+ "status", "refund failure",
+ "code", (json_int_t) ec);
+}
+
+
+/**
+ * Generate refund conflict failure message. Returns the
+ * transaction list @a tl with the details about the conflict.
+ *
+ * @param connection connection to the client
+ * @param tl transaction list showing the conflict
+ * @return MHD result code
+ */
+static int
+reply_refund_conflict (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_TransactionList *tl)
+{
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_CONFLICT,
+ "{s:s, s:I, s:o}",
+ "status", "conflicting refund",
+ "code", (json_int_t)
TALER_EC_REFUND_CONFLICT,
+ "history",
TEH_RESPONSE_compile_transaction_history (tl));
+}
+
+
+/**
+ * Execute a "/refund" transaction. Returns a confirmation that the
+ * refund was successful, or a failure if we are not aware of a
+ * matching /deposit or if it is too late to do the refund.
+ *
+ * IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure with a `const struct TALER_EXCHANGEDB_Refund *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refund_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ const struct TALER_EXCHANGEDB_Refund *refund = cls;
+ struct TALER_EXCHANGEDB_TransactionList *tl;
+ const struct TALER_EXCHANGEDB_Deposit *dep;
+ const struct TALER_EXCHANGEDB_Refund *ref;
+ struct TEH_KS_StateHandle *mks;
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+ struct TALER_Amount expect_fee;
+ enum GNUNET_DB_QueryStatus qs;
+ int deposit_found;
+ int refund_found;
+ int fee_cmp;
+
+ dep = NULL;
+ ref = NULL;
+ tl = NULL;
+ qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+ session,
+ &refund->coin.coin_pub,
+ &tl);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ *mhd_ret = reply_refund_failure (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_REFUND_COIN_NOT_FOUND);
+ return qs;
+ }
+ deposit_found = GNUNET_NO;
+ refund_found = GNUNET_NO;
+ for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
+ NULL != tlp;
+ tlp = tlp->next)
+ {
+ switch (tlp->type)
+ {
+ case TALER_EXCHANGEDB_TT_DEPOSIT:
+ if (GNUNET_NO == deposit_found)
+ {
+ if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
+ &refund->merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) &&
+ (0 == memcmp (&tlp->details.deposit->h_contract_terms,
+ &refund->h_contract_terms,
+ sizeof (struct GNUNET_HashCode))) )
+ {
+ dep = tlp->details.deposit;
+ deposit_found = GNUNET_YES;
+ break;
+ }
+ }
+ break;
+ case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+ /* Melts cannot be refunded, ignore here */
+ break;
+ case TALER_EXCHANGEDB_TT_REFUND:
+ if (GNUNET_NO == refund_found)
+ {
+ /* First, check if existing refund request is identical */
+ if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
+ &refund->merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) &&
+ (0 == memcmp (&tlp->details.refund->h_contract_terms,
+ &refund->h_contract_terms,
+ sizeof (struct GNUNET_HashCode))) &&
+ (tlp->details.refund->rtransaction_id == refund->rtransaction_id)
)
+ {
+ ref = tlp->details.refund;
+ refund_found = GNUNET_YES;
+ break;
+ }
+ /* Second, check if existing refund request conflicts */
+ if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
+ &refund->merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) &&
+ (0 == memcmp (&tlp->details.refund->h_contract_terms,
+ &refund->h_contract_terms,
+ sizeof (struct GNUNET_HashCode))) &&
+ (tlp->details.refund->rtransaction_id != refund->rtransaction_id)
)
+ {
+ GNUNET_break_op (0); /* conflicting refund found */
+ refund_found = GNUNET_SYSERR;
+ /* NOTE: Alternatively we could total up all existing
+ refunds and check if the sum still permits the
+ refund requested (thus allowing multiple, partial
+ refunds). Fow now, we keep it simple. */
+ break;
+ }
+ }
+ break;
+ case TALER_EXCHANGEDB_TT_PAYBACK:
+ /* Paybacks cannot be refunded, ignore here */
+ break;
+ }
+ }
+ /* handle if deposit was NOT found */
+ if (GNUNET_NO == deposit_found)
+ {
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_transaction_unknown (connection,
+
TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* handle if conflicting refund found */
+ if (GNUNET_SYSERR == refund_found)
+ {
+ *mhd_ret = reply_refund_conflict (connection,
+ tl);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* handle if identical refund found */
+ if (GNUNET_YES == refund_found)
+ {
+ /* /refund already done, simply re-transmit confirmation */
+ *mhd_ret = reply_refund_success (connection,
+ ref);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* check currency is compatible */
+ if ( (GNUNET_YES !=
+ TALER_amount_cmp_currency (&refund->refund_amount,
+ &dep->amount_with_fee)) ||
+ (GNUNET_YES !=
+ TALER_amount_cmp_currency (&refund->refund_fee,
+ &dep->deposit_fee)) )
+ {
+ GNUNET_break_op (0); /* currency missmatch */
+ *mhd_ret = reply_refund_failure (connection,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_REFUND_CURRENCY_MISSMATCH);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* check if we already send the money for the /deposit */
+ // FIXME: DB API...
+ qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
+ session,
+ dep);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ /* Internal error, we first had the deposit in the history,
+ but now it is gone? */
+ GNUNET_break (0);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_REFUND_DB_INCONSISTENT,
+ "database inconsistent");
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs; /* go and retry */
+
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ /* money was already transferred to merchant, can no longer refund */
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = reply_refund_failure (connection,
+ MHD_HTTP_GONE,
+ TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* check refund amount is sufficiently low */
+ if (1 == TALER_amount_cmp (&refund->refund_amount,
+ &dep->amount_with_fee) )
+ {
+ GNUNET_break_op (0); /* cannot refund more than original value */
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = reply_refund_failure (connection,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_REFUND_INSUFFICIENT_FUNDS);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ // FIXME: do this outside of transaction function?
+ /* Check refund fee matches fee of denomination key! */
+ mks = TEH_KS_acquire ();
+ dki = TEH_KS_denomination_key_lookup (mks,
+ &dep->coin.denom_pub,
+ TEH_KS_DKU_DEPOSIT);
+ if (NULL == dki)
+ {
+ /* DKI not found, but we do have a coin with this DK in our database;
+ not good... */
+ GNUNET_break (0);
+ TEH_KS_release (mks);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
+ "denomination key not found");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TALER_amount_ntoh (&expect_fee,
+ &dki->issue.properties.fee_refund);
+ fee_cmp = TALER_amount_cmp (&refund->refund_fee,
+ &expect_fee);
+ TEH_KS_release (mks);
+
+ if (-1 == fee_cmp)
+ {
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_REFUND_FEE_TOO_LOW,
+ "refund_fee");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (1 == fee_cmp)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Refund fee proposed by merchant is higher than necessary.\n");
+ }
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+
+ /* Finally, store new refund data */
+ qs = TEH_plugin->insert_refund (TEH_plugin->cls,
+ session,
+ refund);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ TALER_LOG_WARNING ("Failed to store /refund information in database\n");
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_REFUND_STORE_DB_ERROR);
+ return qs;
+ }
+ /* Success or soft failure */
+ return qs;
+}
+
+
+/**
* We have parsed the JSON information about the refund, do some basic
* sanity checks (especially that the signature on the coin is valid)
* and then execute the refund. Note that we need the DB to check
@@ -51,6 +395,7 @@ verify_and_execute_refund (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Refund *refund)
{
struct TALER_RefundRequestPS rr;
+ int mhd_ret;
rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
@@ -90,8 +435,14 @@ verify_and_execute_refund (struct MHD_Connection
*connection,
TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
"merchant_sig");
}
- return TEH_DB_execute_refund (connection,
- refund);
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &refund_transaction,
+ (void *) refund))
+ return mhd_ret;
+ return reply_refund_success (connection,
+ refund);
}
diff --git a/src/exchange/taler-exchange-httpd_reserve.c
b/src/exchange/taler-exchange-httpd_reserve.c
deleted file mode 100644
index 78f8ff1..0000000
--- a/src/exchange/taler-exchange-httpd_reserve.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free
Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
-
- You should have received a copy of the GNU Affero General Public License
along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_reserve.c
- * @brief Handle /reserve/ requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include "taler-exchange-httpd_reserve.h"
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
-
-
-/**
- * Handle a "/reserve/status" request. Parses the
- * given "reserve_pub" argument (which should contain the
- * EdDSA public key of a reserve) and then respond with the
- * status of the reserve.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- struct TALER_ReservePublicKeyP reserve_pub;
- int res;
-
- res = TEH_PARSE_mhd_request_arg_data (connection,
- "reserve_pub",
- &reserve_pub,
- sizeof (struct
TALER_ReservePublicKeyP));
- if (GNUNET_SYSERR == res)
- return MHD_NO; /* internal error */
- if (GNUNET_NO == res)
- return MHD_YES; /* parse error */
- return TEH_DB_execute_reserve_status (connection,
- &reserve_pub);
-}
-
-
-/**
- * Handle a "/reserve/withdraw" request. Parses the "reserve_pub"
- * EdDSA key of the reserve and the requested "denom_pub" which
- * specifies the key/value of the coin to be withdrawn, and checks
- * that the signature "reserve_sig" makes this a valid withdrawal
- * request from the specified reserve. If so, the envelope
- * with the blinded coin "coin_ev" is passed down to execute the
- * withdrawl operation.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- json_t *root;
- struct TALER_WithdrawRequestPS wsrd;
- int res;
- struct TALER_DenominationPublicKey denomination_pub;
- char *blinded_msg;
- size_t blinded_msg_len;
- struct TALER_Amount amount;
- struct TALER_Amount amount_with_fee;
- struct TALER_Amount fee_withdraw;
- struct TALER_ReserveSignatureP signature;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- struct TEH_KS_StateHandle *ks;
-
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_varsize ("coin_ev",
- (void **) &blinded_msg,
- &blinded_msg_len),
- GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &wsrd.reserve_pub),
- GNUNET_JSON_spec_fixed_auto ("reserve_sig",
- &signature),
- TALER_JSON_spec_denomination_public_key ("denom_pub",
- &denomination_pub),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- if ( (GNUNET_NO == res) || (NULL == root) )
- return MHD_YES;
- res = TEH_PARSE_json_data (connection,
- root,
- spec);
- json_decref (root);
- if (GNUNET_OK != res)
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- ks = TEH_KS_acquire ();
- dki = TEH_KS_denomination_key_lookup (ks,
- &denomination_pub,
- TEH_KS_DKU_WITHDRAW);
- if (NULL == dki)
- {
- GNUNET_JSON_parse_free (spec);
- TEH_KS_release (ks);
- return TEH_RESPONSE_reply_arg_unknown (connection,
-
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND,
- "denom_pub");
- }
- TALER_amount_ntoh (&amount,
- &dki->issue.properties.value);
- TALER_amount_ntoh (&fee_withdraw,
- &dki->issue.properties.fee_withdraw);
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&amount_with_fee,
- &amount,
- &fee_withdraw));
- TALER_amount_hton (&wsrd.amount_with_fee,
- &amount_with_fee);
- TALER_amount_hton (&wsrd.withdraw_fee,
- &fee_withdraw);
- TEH_KS_release (ks);
- /* verify signature! */
- wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
- wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
-
- GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key,
- &wsrd.h_denomination_pub);
- GNUNET_CRYPTO_hash (blinded_msg,
- blinded_msg_len,
- &wsrd.h_coin_envelope);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
- &wsrd.purpose,
- &signature.eddsa_signature,
- &wsrd.reserve_pub.eddsa_pub))
- {
- TALER_LOG_WARNING ("Client supplied invalid signature for
/reserve/withdraw request\n");
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_signature_invalid (connection,
-
TALER_EC_WITHDRAW_RESERVE_SIGNATURE_INVALID,
- "reserve_sig");
- }
- res = TEH_DB_execute_reserve_withdraw (connection,
- &wsrd.reserve_pub,
- &denomination_pub,
- blinded_msg,
- blinded_msg_len,
- &signature);
- GNUNET_JSON_parse_free (spec);
- return res;
-}
-
-/* end of taler-exchange-httpd_reserve.c */
diff --git a/src/exchange/taler-exchange-httpd_reserve_status.c
b/src/exchange/taler-exchange-httpd_reserve_status.c
new file mode 100644
index 0000000..f87afa5
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserve_status.c
@@ -0,0 +1,167 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserve_status.c
+ * @brief Handle /reserve/status requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler-exchange-httpd_reserve_status.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Send reserve status information to client.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+static int
+reply_reserve_status_success (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_ReserveHistory *rh)
+{
+ json_t *json_balance;
+ json_t *json_history;
+ struct TALER_Amount balance;
+
+ json_history = TEH_RESPONSE_compile_reserve_history (rh,
+ &balance);
+ if (NULL == json_history)
+ return TEH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_RESERVE_STATUS_DB_ERROR,
+ "balance calculation failure");
+ json_balance = TALER_JSON_from_amount (&balance);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o}",
+ "balance", json_balance,
+ "history", json_history);
+}
+
+
+/**
+ * Closure for #reserve_status_transaction.
+ */
+struct ReserveStatusContext
+{
+ /**
+ * Public key of the reserve the inquiry is about.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * History of the reserve, set in the callback.
+ */
+ struct TALER_EXCHANGEDB_ReserveHistory *rh;
+
+};
+
+
+/**
+ * Function implementing /reserve/status transaction.
+ * Execute a /reserve/status. Given the public key of a reserve,
+ * return the associated transaction history. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response. IF it returns an hard error,
+ * the transaction logic MUST queue a MHD response and set @a mhd_ret.
+ * IF it returns the soft error code, the function MAY be called again
+ * to retry and MUST not queue a MHD response.
+ *
+ * @param cls a `struct ReserveStatusContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+reserve_status_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct ReserveStatusContext *rsc = cls;
+
+ return TEH_plugin->get_reserve_history (TEH_plugin->cls,
+ session,
+ &rsc->reserve_pub,
+ &rsc->rh);
+}
+
+
+/**
+ * Handle a "/reserve/status" request. Parses the
+ * given "reserve_pub" argument (which should contain the
+ * EdDSA public key of a reserve) and then respond with the
+ * status of the reserve.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct ReserveStatusContext rsc;
+ int res;
+ int mhd_ret;
+
+ res = TEH_PARSE_mhd_request_arg_data (connection,
+ "reserve_pub",
+ &rsc.reserve_pub,
+ sizeof (struct
TALER_ReservePublicKeyP));
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* internal error */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* parse error */
+ rsc.rh = NULL;
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &reserve_status_transaction,
+ &rsc))
+ return mhd_ret;
+
+ /* generate proper response */
+ if (NULL == rsc.rh)
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s, s:s}",
+ "error", "Reserve not found",
+ "parameter", "withdraw_pub");
+ mhd_ret = reply_reserve_status_success (connection,
+ rsc.rh);
+ TEH_plugin->free_reserve_history (TEH_plugin->cls,
+ rsc.rh);
+ return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_reserve_status.c */
diff --git a/src/exchange/taler-exchange-httpd_refund.h
b/src/exchange/taler-exchange-httpd_reserve_status.h
similarity index 60%
copy from src/exchange/taler-exchange-httpd_refund.h
copy to src/exchange/taler-exchange-httpd_reserve_status.h
index 1fa52b1..7bfd4dd 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_reserve_status.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,26 +14,23 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_refund.h
- * @brief Handle /refund requests
+ * @file taler-exchange-httpd_reserve_status.h
+ * @brief Handle /reserve/status requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_REFUND_H
-#define TALER_EXCHANGE_HTTPD_REFUND_H
+#ifndef TALER_EXCHANGE_HTTPD_RESERVE_STATUS_H
+#define TALER_EXCHANGE_HTTPD_RESERVE_STATUS_H
-#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
#include "taler-exchange-httpd.h"
-
/**
- * Handle a "/refund" request. Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_refund() to
- * further check the details of the operation specified. If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/reserve/status" request. Parses the
+ * given "reserve_pub" argument (which should contain the
+ * EdDSA public key of a reserve) and then respond with the
+ * status of the reserve.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
@@ -43,10 +40,10 @@
* @return MHD result code
*/
int
-TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
+TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
#endif
diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c
b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
new file mode 100644
index 0000000..2bc268d
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
@@ -0,0 +1,506 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserve_withdraw.c
+ * @brief Handle /reserve/withdraw requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler-exchange-httpd_reserve_withdraw.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Send reserve status information to client with the
+ * message that we have insufficient funds for the
+ * requested /reserve/withdraw operation.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+static int
+reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
+ const struct
TALER_EXCHANGEDB_ReserveHistory *rh)
+{
+ json_t *json_balance;
+ json_t *json_history;
+ struct TALER_Amount balance;
+
+ json_history = TEH_RESPONSE_compile_reserve_history (rh,
+ &balance);
+ if (NULL == json_history)
+ return TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
+ "balance calculation failure");
+ json_balance = TALER_JSON_from_amount (&balance);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_FORBIDDEN,
+ "{s:s, s:I, s:o, s:o}",
+ "error", "Insufficient funds",
+ "code", (json_int_t)
TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
+ "balance", json_balance,
+ "history", json_history);
+}
+
+
+/**
+ * Send blinded coin information to client.
+ *
+ * @param connection connection to the client
+ * @param collectable blinded coin to return
+ * @return MHD result code
+ */
+static int
+reply_reserve_withdraw_success (struct MHD_Connection *connection,
+ const struct
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
+{
+ json_t *sig_json;
+
+ sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o}",
+ "ev_sig", sig_json);
+}
+
+
+/**
+ * Context for #withdraw_transaction.
+ */
+struct WithdrawContext
+{
+ /**
+ * Details about the withdrawal request.
+ */
+ struct TALER_WithdrawRequestPS wsrd;
+
+ /**
+ * Value of the coin plus withdraw fee.
+ */
+ struct TALER_Amount amount_required;
+
+ /**
+ * Denomination public key.
+ */
+ struct TALER_DenominationPublicKey denomination_pub;
+
+ /**
+ * Signature over the request.
+ */
+ struct TALER_ReserveSignatureP signature;
+
+ /**
+ * Blinded planchet.
+ */
+ char *blinded_msg;
+
+ /**
+ * Key state to use to inspect previous withdrawal values.
+ */
+ struct TEH_KS_StateHandle *key_state;
+
+ /**
+ * Number of bytes in @e blinded_msg.
+ */
+ size_t blinded_msg_len;
+
+ /**
+ * Details about denomination we are about to withdraw.
+ */
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+
+ /**
+ * Set to the resulting signed coin data to be returned to the client.
+ */
+ struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
+
+};
+
+
+/**
+ * Function implementing /reserve/withdraw transaction. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response. IF it returns an hard error,
+ * the transaction logic MUST queue a MHD response and set @a mhd_ret.
+ * IF it returns the soft error code, the function MAY be called again
+ * to retry and MUST not queue a MHD response.
+ *
+ * @param cls a `struct WithdrawContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+withdraw_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct WithdrawContext *wc = cls;
+ struct TALER_EXCHANGEDB_ReserveHistory *rh;
+ struct TALER_Amount deposit_total;
+ struct TALER_Amount withdraw_total;
+ struct TALER_Amount balance;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ int res;
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_DenominationSignature denom_sig;
+ struct GNUNET_HashCode h_blind;
+
+ GNUNET_CRYPTO_hash (wc->blinded_msg,
+ wc->blinded_msg_len,
+ &h_blind);
+ qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
+ session,
+ &h_blind,
+ &wc->collectable);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
+ return qs;
+ }
+
+ /* Don't sign again if we have already signed the coin */
+ if (1 == qs)
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ GNUNET_assert (0 == qs);
+
+ /* Check if balance is sufficient */
+ qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
+ session,
+ &wc->wsrd.reserve_pub,
+ &rh);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
+ return qs;
+ }
+ if (NULL == rh)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
+
TALER_EC_WITHDRAW_RESERVE_UNKNOWN,
+ "reserve_pub");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* calculate balance of the reserve */
+ res = 0;
+ for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
+ NULL != pos;
+ pos = pos->next)
+ {
+ switch (pos->type)
+ {
+ case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
+ if (0 == (res & 1))
+ deposit_total = pos->details.bank->amount;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&deposit_total,
+ &deposit_total,
+ &pos->details.bank->amount))
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ res |= 1;
+ break;
+ case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
+ {
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *tdki;
+
+ tdki = TEH_KS_denomination_key_lookup (wc->key_state,
+
&pos->details.withdraw->denom_pub,
+ TEH_KS_DKU_WITHDRAW);
+ if (NULL == tdki)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TALER_amount_ntoh (&value,
+ &tdki->issue.properties.value);
+ if (0 == (res & 2))
+ withdraw_total = value;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&withdraw_total,
+ &withdraw_total,
+ &value))
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ res |= 2;
+ break;
+ }
+ case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
+ if (0 == (res & 1))
+ deposit_total = pos->details.payback->value;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&deposit_total,
+ &deposit_total,
+ &pos->details.payback->value))
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ res |= 1;
+ break;
+
+ case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
+ if (0 == (res & 2))
+ withdraw_total = pos->details.bank->amount;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&withdraw_total,
+ &withdraw_total,
+ &pos->details.bank->amount))
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ res |= 2;
+ break;
+ }
+ }
+ if (0 == (res & 1))
+ {
+ /* did not encounter any wire transfer operations, how can we have a
reserve? */
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (0 == (res & 2))
+ {
+ /* did not encounter any withdraw operations, set to zero */
+ TALER_amount_get_zero (deposit_total.currency,
+ &withdraw_total);
+ }
+ /* All reserve balances should be non-negative */
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&balance,
+ &deposit_total,
+ &withdraw_total))
+ {
+ GNUNET_break (0); /* database inconsistent */
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (0 < TALER_amount_cmp (&wc->amount_required,
+ &balance))
+ {
+ *mhd_ret = reply_reserve_withdraw_insufficient_funds (connection,
+ rh);
+ TEH_plugin->free_reserve_history (TEH_plugin->cls,
+ rh);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TEH_plugin->free_reserve_history (TEH_plugin->cls,
+ rh);
+
+ /* Balance is good, sign the coin! */
+ denom_sig.rsa_signature
+ = GNUNET_CRYPTO_rsa_sign_blinded (wc->dki->denom_priv.rsa_private_key,
+ wc->blinded_msg,
+ wc->blinded_msg_len);
+ if (NULL == denom_sig.rsa_signature)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
+ "Internal error");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ wc->collectable.sig = denom_sig;
+ wc->collectable.denom_pub = wc->denomination_pub;
+ wc->collectable.amount_with_fee = wc->amount_required;
+ wc->collectable.withdraw_fee = fee_withdraw;
+ wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
+ wc->collectable.h_coin_envelope = h_blind;
+ wc->collectable.reserve_sig = wc->signature;
+ qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
+ session,
+ &wc->collectable);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_WITHDRAW_DB_STORE_ERROR);
+ return qs;
+ }
+ return qs;
+}
+
+
+/**
+ * Handle a "/reserve/withdraw" request. Parses the "reserve_pub"
+ * EdDSA key of the reserve and the requested "denom_pub" which
+ * specifies the key/value of the coin to be withdrawn, and checks
+ * that the signature "reserve_sig" makes this a valid withdrawal
+ * request from the specified reserve. If so, the envelope
+ * with the blinded coin "coin_ev" is passed down to execute the
+ * withdrawl operation.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct WithdrawContext wc;
+ json_t *root;
+ int res;
+ int mhd_ret;
+ struct TALER_Amount amount;
+ struct TALER_Amount fee_withdraw;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_varsize ("coin_ev",
+ (void **) &wc.blinded_msg,
+ &wc.blinded_msg_len),
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &wc.wsrd.reserve_pub),
+ GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+ &wc.signature),
+ TALER_JSON_spec_denomination_public_key ("denom_pub",
+ &wc.denomination_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == root) )
+ return MHD_YES;
+ res = TEH_PARSE_json_data (connection,
+ root,
+ spec);
+ json_decref (root);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ wc.key_state = TEH_KS_acquire ();
+ wc.dki = TEH_KS_denomination_key_lookup (wc.key_state,
+ &wc.denomination_pub,
+ TEH_KS_DKU_WITHDRAW);
+ if (NULL == wc.dki)
+ {
+ GNUNET_JSON_parse_free (spec);
+ TEH_KS_release (wc.key_state);
+ return TEH_RESPONSE_reply_arg_unknown (connection,
+
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND,
+ "denom_pub");
+ }
+ TALER_amount_ntoh (&amount,
+ &wc.dki->issue.properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &wc.dki->issue.properties.fee_withdraw);
+ if (GNUNET_OK !=
+ TALER_amount_add (&wc.amount_required,
+ &amount,
+ &fee_withdraw))
+ {
+ GNUNET_JSON_parse_free (spec);
+ TEH_KS_release (wc.key_state);
+ return TEH_RESPONSE_reply_internal_error (connection,
+
TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW,
+ "amount overflow for value plus
withdraw fee");
+ }
+ TALER_amount_hton (&wc.wsrd.amount_with_fee,
+ &wc.amount_required);
+ TALER_amount_hton (&wc.wsrd.withdraw_fee,
+ &fee_withdraw);
+ /* verify signature! */
+ wc.wsrd.purpose.size
+ = htonl (sizeof (struct TALER_WithdrawRequestPS));
+ wc.wsrd.purpose.purpose
+ = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+ GNUNET_CRYPTO_rsa_public_key_hash (wc.denomination_pub.rsa_public_key,
+ &wc.wsrd.h_denomination_pub);
+ GNUNET_CRYPTO_hash (wc.blinded_msg,
+ wc.blinded_msg_len,
+ &wc.wsrd.h_coin_envelope);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
+ &wc.wsrd.purpose,
+ &wc.signature.eddsa_signature,
+ &wc.wsrd.reserve_pub.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Client supplied invalid signature for
/reserve/withdraw request\n");
+ GNUNET_JSON_parse_free (spec);
+ TEH_KS_release (wc.key_state);
+ return TEH_RESPONSE_reply_signature_invalid (connection,
+
TALER_EC_WITHDRAW_RESERVE_SIGNATURE_INVALID,
+ "reserve_sig");
+ }
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &withdraw_transaction,
+ &wc))
+ {
+ TEH_KS_release (wc.key_state);
+ GNUNET_JSON_parse_free (spec);
+ return mhd_ret;
+ }
+ TEH_KS_release (wc.key_state);
+ GNUNET_JSON_parse_free (spec);
+
+ mhd_ret = reply_reserve_withdraw_success (connection,
+ &wc.collectable);
+ GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
+ return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_reserve_withdraw.c */
diff --git a/src/exchange/taler-exchange-httpd_reserve.h
b/src/exchange/taler-exchange-httpd_reserve_withdraw.h
similarity index 64%
rename from src/exchange/taler-exchange-httpd_reserve.h
rename to src/exchange/taler-exchange-httpd_reserve_withdraw.h
index 1db7ea7..0d5914d 100644
--- a/src/exchange/taler-exchange-httpd_reserve.h
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,38 +14,18 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_reserve.h
- * @brief Handle /reserve/ requests
+ * @file taler-exchange-httpd_reserve_withdraw.h
+ * @brief Handle /reserve/withdraw requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_RESERVE_H
-#define TALER_EXCHANGE_HTTPD_RESERVE_H
+#ifndef TALER_EXCHANGE_HTTPD_RESERVE_WITHDRAW_H
+#define TALER_EXCHANGE_HTTPD_RESERVE_WITHDRAW_H
#include <microhttpd.h>
#include "taler-exchange-httpd.h"
-/**
- * Handle a "/reserve/status" request. Parses the
- * given "reserve_pub" argument (which should contain the
- * EdDSA public key of a reserve) and then respond with the
- * status of the reserve.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
/**
* Handle a "/reserve/withdraw" request. Parses the "reserve_pub"
diff --git a/src/exchange/taler-exchange-httpd_responses.c
b/src/exchange/taler-exchange-httpd_responses.c
index 01b5606..c31c248 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -15,7 +15,7 @@
*/
/**
* @file taler-exchange-httpd_responses.c
- * @brief API for generating the various replies of the exchange; these
+ * @brief API for generating genric replies of the exchange; these
* functions are called TEH_RESPONSE_reply_ and they generate
* and queue MHD response objects for a given connection.
* @author Florian Dold
@@ -462,67 +462,13 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection
*connection)
/**
- * Send confirmation of deposit success to client. This function
- * will create a signed message affirming the given information
- * and return it to the client. By this, the exchange affirms that
- * the coin had sufficient (residual) value for the specified
- * transaction and that it will execute the requested deposit
- * operation with the given wiring details.
- *
- * @param connection connection to the client
- * @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 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
- * @return MHD result code
- */
-int
-TEH_RESPONSE_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
refund_deadline,
- const struct TALER_MerchantPublicKeyP
*merchant,
- const struct TALER_Amount
*amount_without_fee)
-{
- struct TALER_DepositConfirmationPS dc;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
-
- dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
- dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
- dc.h_contract_terms = *h_contract_terms;
- dc.h_wire = *h_wire;
- dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
- dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
- TALER_amount_hton (&dc.amount_without_fee,
- amount_without_fee);
- dc.coin_pub = *coin_pub;
- dc.merchant = *merchant;
- TEH_KS_sign (&dc.purpose,
- &pub,
- &sig);
- return TEH_RESPONSE_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));
-}
-
-
-/**
* Compile the transaction history of a coin into a JSON object.
*
* @param tl transaction history to JSON-ify
* @return json representation of the @a rh
*/
-static json_t *
-compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
+json_t *
+TEH_RESPONSE_compile_transaction_history (const struct
TALER_EXCHANGEDB_TransactionList *tl)
{
json_t *history;
@@ -717,7 +663,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct
MHD_Connection *connection,
{
json_t *history;
- history = compile_transaction_history (tl);
+ history = TEH_RESPONSE_compile_transaction_history (tl);
if (NULL == history)
return TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
@@ -739,9 +685,9 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct
MHD_Connection *connection,
* @param[out] balance set to current reserve balance
* @return json representation of the @a rh, NULL on error
*/
-static json_t *
-compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
- struct TALER_Amount *balance)
+json_t *
+TEH_RESPONSE_compile_reserve_history (const struct
TALER_EXCHANGEDB_ReserveHistory *rh,
+ struct TALER_Amount *balance)
{
struct TALER_Amount deposit_total;
struct TALER_Amount withdraw_total;
@@ -933,416 +879,6 @@ compile_reserve_history (const struct
TALER_EXCHANGEDB_ReserveHistory *rh,
/**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_TransactionList *tl)
-{
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_CONFLICT,
- "{s:s, s:I, s:o}",
- "status", "conflicting refund",
- "code", (json_int_t)
TALER_EC_REFUND_CONFLICT,
- "history", compile_transaction_history
(tl));
-}
-
-
-/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code. The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec taler error code to include
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
- unsigned int response_code,
- enum TALER_ErrorCode ec)
-{
- return TEH_RESPONSE_reply_json_pack (connection,
- response_code,
- "{s:s, s:I}",
- "status", "refund failure",
- "code", (json_int_t) ec);
-}
-
-
-/**
- * Generate successful refund confirmation message.
- *
- * @param connection connection to the client
- * @param refund details about the successful refund
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Refund
*refund)
-{
- struct TALER_RefundConfirmationPS rc;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
-
- rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
- rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
- rc.h_contract_terms = refund->h_contract_terms;
- rc.coin_pub = refund->coin.coin_pub;
- rc.merchant = refund->merchant_pub;
- rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
- TALER_amount_hton (&rc.refund_amount,
- &refund->refund_amount);
- TALER_amount_hton (&rc.refund_fee,
- &refund->refund_fee);
- TEH_KS_sign (&rc.purpose,
- &pub,
- &sig);
- return TEH_RESPONSE_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));
-}
-
-
-/**
- * Send reserve status information to client.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_ReserveHistory *rh)
-{
- json_t *json_balance;
- json_t *json_history;
- struct TALER_Amount balance;
-
- json_history = compile_reserve_history (rh,
- &balance);
- if (NULL == json_history)
- return TEH_RESPONSE_reply_internal_error (connection,
- TALER_EC_RESERVE_STATUS_DB_ERROR,
- "balance calculation failure");
- json_balance = TALER_JSON_from_amount (&balance);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o, s:o}",
- "balance", json_balance,
- "history", json_history);
-}
-
-
-/**
- * Send reserve status information to client with the
- * message that we have insufficient funds for the
- * requested /reserve/withdraw operation.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection
*connection,
- const struct
TALER_EXCHANGEDB_ReserveHistory *rh)
-{
- json_t *json_balance;
- json_t *json_history;
- struct TALER_Amount balance;
-
- json_history = compile_reserve_history (rh,
- &balance);
- if (NULL == json_history)
- return TEH_RESPONSE_reply_internal_error (connection,
-
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
- "balance calculation failure");
- json_balance = TALER_JSON_from_amount (&balance);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_FORBIDDEN,
- "{s:s, s:I, s:o, s:o}",
- "error", "Insufficient funds",
- "code", (json_int_t)
TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
- "balance", json_balance,
- "history", json_history);
-}
-
-
-/**
- * Send blinded coin information to client.
- *
- * @param connection connection to the client
- * @param collectable blinded coin to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
-{
- json_t *sig_json;
-
- sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o}",
- "ev_sig", sig_json);
-}
-
-
-/**
- * Send a response for a failed "/refresh/melt" request. The
- * transaction history of the given coin demonstrates that the
- * @a residual value of the coin is below the @a requested
- * contribution of the coin for the melt. Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute, including
fee
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection
*connection,
- const struct
TALER_CoinSpendPublicKeyP *coin_pub,
- struct TALER_Amount
coin_value,
- struct
TALER_EXCHANGEDB_TransactionList *tl,
- struct TALER_Amount
requested,
- struct TALER_Amount
residual)
-{
- json_t *history;
-
- history = compile_transaction_history (tl);
- if (NULL == history)
- return TEH_RESPONSE_reply_internal_db_error (connection,
-
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_FORBIDDEN,
- "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
- "error",
- "insufficient funds",
- "code",
- (json_int_t)
TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
- "coin_pub",
- GNUNET_JSON_from_data_auto (coin_pub),
- "original_value",
- TALER_JSON_from_amount (&coin_value),
- "residual_value",
- TALER_JSON_from_amount (&residual),
- "requested_value",
- TALER_JSON_from_amount (&requested),
- "history",
- history);
-}
-
-
-/**
- * Send a response to a "/refresh/melt" request.
- *
- * @param connection the connection to send the response to
- * @param session_hash hash of the refresh session
- * @param noreveal_index which index will the client not have to reveal
- * @return a MHD status code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
- const struct GNUNET_HashCode
*session_hash,
- uint16_t noreveal_index)
-{
- struct TALER_RefreshMeltConfirmationPS body;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
- json_t *sig_json;
-
- body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
- body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
- body.session_hash = *session_hash;
- body.noreveal_index = htons (noreveal_index);
- body.reserved = htons (0);
- TEH_KS_sign (&body.purpose,
- &pub,
- &sig);
- sig_json = GNUNET_JSON_from_data_auto (&sig);
- GNUNET_assert (NULL != sig_json);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:i, s:o, s:o}",
- "noreveal_index", (int) noreveal_index,
- "exchange_sig", sig_json,
- "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
- unsigned int num_newcoins,
- const struct
TALER_DenominationSignature *sigs)
-{
- int newcoin_index;
- json_t *root;
- json_t *obj;
- json_t *list;
- int ret;
-
- list = json_array ();
- for (newcoin_index = 0; newcoin_index < num_newcoins; newcoin_index++)
- {
- obj = json_object ();
- json_object_set_new (obj,
- "ev_sig",
- GNUNET_JSON_from_rsa_signature
(sigs[newcoin_index].rsa_signature));
- GNUNET_assert (0 ==
- json_array_append_new (list,
- obj));
- }
- root = json_object ();
- json_object_set_new (root,
- "ev_sigs",
- list);
- ret = TEH_RESPONSE_reply_json (connection,
- root,
- MHD_HTTP_OK);
- json_decref (root);
- return ret;
-}
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param session info about session
- * @param commit_coins array of @a num_newcoins committed envelopes at offset
@a gamma
- * @param denom_pubs array of @a num_newcoins denomination keys for the new
coins
- * @param gamma_tp transfer public key at offset @a gamma
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_RefreshSession *session,
- const struct
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
- const struct
TALER_DenominationPublicKey *denom_pubs,
- const struct
TALER_TransferPublicKeyP *gamma_tp)
-{
- json_t *info_new;
- json_t *info_commit_k;
- unsigned int i;
-
- info_new = json_array ();
- info_commit_k = json_array ();
- for (i=0;i<session->num_newcoins;i++)
- {
- const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
- json_t *cc_json;
-
- GNUNET_assert (0 ==
- json_array_append_new (info_new,
- GNUNET_JSON_from_rsa_public_key
(denom_pubs[i].rsa_public_key)));
-
- cc = &commit_coins[i];
- cc_json = json_pack ("{s:o}",
- "coin_ev",
- GNUNET_JSON_from_data (cc->coin_ev,
- cc->coin_ev_size));
- GNUNET_assert (0 ==
- json_array_append_new (info_commit_k,
- cc_json));
- }
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_CONFLICT,
- "{s:s, s:I, s:o, s:o, s:o, s:o, s:o,
s:o, s:o, s:i}",
- "error", "commitment violation",
- "code", (json_int_t)
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
- "coin_sig", GNUNET_JSON_from_data_auto
(&session->melt.coin_sig),
- "coin_pub", GNUNET_JSON_from_data_auto
(&session->melt.coin.coin_pub),
- "melt_amount_with_fee",
TALER_JSON_from_amount (&session->melt.amount_with_fee),
- "melt_fee", TALER_JSON_from_amount
(&session->melt.melt_fee),
- "newcoin_infos", info_new,
- "commit_infos", info_commit_k,
- "gamma_tp", GNUNET_JSON_from_data_auto
(gamma_tp),
- "gamma", (int) session->noreveal_index);
-}
-
-
-/**
- * Send a response for "/refresh/link".
- *
- * @param connection the connection to send the response to
- * @param num_sessions number of sessions the coin was used in
- * @param sessions array of @a num_session entries with
- * information for each session
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
- unsigned int num_sessions,
- const struct
TEH_RESPONSE_LinkSessionInfo *sessions)
-{
- json_t *root;
- json_t *mlist;
- int res;
- unsigned int i;
-
- mlist = json_array ();
- for (i=0;i<num_sessions;i++)
- {
- const struct TALER_EXCHANGEDB_LinkDataList *pos;
- json_t *list = json_array ();
-
- for (pos = sessions[i].ldl; NULL != pos; pos = pos->next)
- {
- json_t *obj;
-
- obj = json_object ();
- json_object_set_new (obj,
- "denom_pub",
- GNUNET_JSON_from_rsa_public_key
(pos->denom_pub.rsa_public_key));
- json_object_set_new (obj,
- "ev_sig",
- GNUNET_JSON_from_rsa_signature
(pos->ev_sig.rsa_signature));
- GNUNET_assert (0 ==
- json_array_append_new (list,
- obj));
- }
- root = json_object ();
- json_object_set_new (root,
- "new_coins",
- list);
- json_object_set_new (root,
- "transfer_pub",
- GNUNET_JSON_from_data_auto
(&sessions[i].transfer_pub));
- GNUNET_assert (0 ==
- json_array_append_new (mlist,
- root));
- }
- res = TEH_RESPONSE_reply_json (connection,
- mlist,
- MHD_HTTP_OK);
- json_decref (mlist);
- return res;
-}
-
-
-/**
* A merchant asked for details about a deposit, but
* we do not know anything about the deposit. Generate the
* 404 reply.
@@ -1363,217 +899,4 @@ TEH_RESPONSE_reply_transaction_unknown (struct
MHD_Connection *connection,
}
-/**
- * A merchant asked for details about a deposit, but
- * we did not execute the deposit yet. Generate a 202 reply.
- *
- * @param connection connection to the client
- * @param planned_exec_time planned execution time
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection,
- struct GNUNET_TIME_Absolute
planned_exec_time)
-{
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_ACCEPTED,
- "{s:o}",
- "execution_time",
GNUNET_JSON_from_time_abs (planned_exec_time));
-}
-
-
-/**
- * A merchant asked for details about a deposit. Provide
- * them. Generates the 200 reply.
- *
- * @param connection connection to the client
- * @param h_contract_terms hash of the contract
- * @param h_wire hash of wire account details
- * @param coin_pub public key of the coin
- * @param coin_contribution how much did the coin we asked about
- * contribute to the total transfer value? (deposit value minus fee)
- * @param wtid raw wire transfer identifier
- * @param exec_time execution time of the wire transfer
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection,
- const struct GNUNET_HashCode
*h_contract_terms,
- const struct GNUNET_HashCode *h_wire,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub,
- const struct TALER_Amount
*coin_contribution,
- const struct
TALER_WireTransferIdentifierRawP *wtid,
- struct GNUNET_TIME_Absolute exec_time)
-{
- struct TALER_ConfirmWirePS cw;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
-
- cw.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE);
- cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
- cw.h_wire = *h_wire;
- cw.h_contract_terms = *h_contract_terms;
- cw.wtid = *wtid;
- cw.coin_pub = *coin_pub;
- cw.execution_time = GNUNET_TIME_absolute_hton (exec_time);
- TALER_amount_hton (&cw.coin_contribution,
- coin_contribution);
- TEH_KS_sign (&cw.purpose,
- &pub,
- &sig);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o, s:o, s:o, s:o, s:o}",
- "wtid", GNUNET_JSON_from_data_auto
(wtid),
- "execution_time",
GNUNET_JSON_from_time_abs (exec_time),
- "coin_contribution",
TALER_JSON_from_amount (coin_contribution),
- "exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
- "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-/**
- * A merchant asked for transaction details about a wire transfer.
- * Provide them. Generates the 200 reply.
- *
- * @param connection connection to the client
- * @param total total amount that was transferred
- * @param merchant_pub public key of the merchant
- * @param h_wire destination account
- * @param wire_fee wire fee that was charged
- * @param exec_time execution time of the wire transfer
- * @param wdd_head linked list with details about the combined deposits
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
- const struct TALER_Amount *total,
- const struct
TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode
*h_wire,
- const struct TALER_Amount *wire_fee,
- struct GNUNET_TIME_Absolute
exec_time,
- const struct
TEH_TrackTransferDetail *wdd_head)
-{
- const struct TEH_TrackTransferDetail *wdd_pos;
- json_t *deposits;
- struct TALER_WireDepositDetailP dd;
- struct GNUNET_HashContext *hash_context;
- struct TALER_WireDepositDataPS wdp;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
-
- GNUNET_TIME_round_abs (&exec_time);
- deposits = json_array ();
- hash_context = GNUNET_CRYPTO_hash_context_start ();
- for (wdd_pos = wdd_head; NULL != wdd_pos; wdd_pos = wdd_pos->next)
- {
- dd.h_contract_terms = wdd_pos->h_contract_terms;
- dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
- dd.coin_pub = wdd_pos->coin_pub;
- TALER_amount_hton (&dd.deposit_value,
- &wdd_pos->deposit_value);
- TALER_amount_hton (&dd.deposit_fee,
- &wdd_pos->deposit_fee);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &dd,
- sizeof (struct TALER_WireDepositDetailP));
- GNUNET_assert (0 ==
- json_array_append_new (deposits,
- json_pack ("{s:o, s:o, s:o, s:o}",
- "h_contract_terms",
GNUNET_JSON_from_data_auto (&wdd_pos->h_contract_terms),
- "coin_pub",
GNUNET_JSON_from_data_auto (&wdd_pos->coin_pub),
- "deposit_value",
TALER_JSON_from_amount (&wdd_pos->deposit_value),
- "deposit_fee",
TALER_JSON_from_amount (&wdd_pos->deposit_fee))));
- }
- wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
- wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
- TALER_amount_hton (&wdp.total,
- total);
- 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);
- TEH_KS_sign (&wdp.purpose,
- &pub,
- &sig);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o, s:o, s:o, s:o, s:o, s:o, s:o,
s:o}",
- "total", TALER_JSON_from_amount (total),
- "wire_fee", TALER_JSON_from_amount
(wire_fee),
- "merchant_pub",
GNUNET_JSON_from_data_auto (merchant_pub),
- "H_wire", GNUNET_JSON_from_data_auto
(h_wire),
- "execution_time",
GNUNET_JSON_from_time_abs (exec_time),
- "deposits", deposits,
- "exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
- "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-
-/**
- * A wallet asked for /payback, but we do not know anything about the
- * original withdraw operation specified. Generates a 404 reply.
- *
- * @param connection connection to the client
- * @param ec Taler error code
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection,
- enum TALER_ErrorCode ec)
-{
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_NOT_FOUND,
- "{s:s, s:I}",
- "error", "blinded coin unknown",
- "code", (json_int_t) ec);
-}
-
-
-/**
- * A wallet asked for /payback, return the successful response.
- *
- * @param connection connection to the client
- * @param coin_pub coin for which we are processing the payback request
- * @param reserve_pub public key of the reserve that will receive the payback
- * @param amount the amount we will wire back
- * @param timestamp when did the exchange receive the /payback request
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub,
- const struct TALER_ReservePublicKeyP
*reserve_pub,
- const struct TALER_Amount *amount,
- struct GNUNET_TIME_Absolute timestamp)
-{
- struct TALER_PaybackConfirmationPS pc;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
-
- pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
- pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
- pc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
- TALER_amount_hton (&pc.payback_amount,
- amount);
- pc.coin_pub = *coin_pub;
- pc.reserve_pub = *reserve_pub;
- TEH_KS_sign (&pc.purpose,
- &pub,
- &sig);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:o, s:o, s:o, s:o, s:o}",
- "reserve_pub",
GNUNET_JSON_from_data_auto (reserve_pub),
- "timestamp", GNUNET_JSON_from_time_abs
(timestamp),
- "amount", TALER_JSON_from_amount
(amount),
- "exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
- "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-
/* end of taler-exchange-httpd_responses.c */
diff --git a/src/exchange/taler-exchange-httpd_responses.h
b/src/exchange/taler-exchange-httpd_responses.h
index 6a33b65..1e504fd 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -16,7 +16,7 @@
/**
* @file taler-exchange-httpd_responses.h
- * @brief API for generating the various replies of the exchange; these
+ * @brief API for generating generic replies of the exchange; these
* functions are called TEH_RESPONSE_reply_ and they generate
* and queue MHD response objects for a given connection.
* @author Florian Dold
@@ -244,32 +244,16 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection
*connectionx);
/**
- * Send confirmation of deposit success to client. This function
- * will create a signed message affirming the given information
- * and return it to the client. By this, the exchange affirms that
- * the coin had sufficient (residual) value for the specified
- * transaction and that it will execute the requested deposit
- * operation with the given wiring details.
+ * Compile the history of a reserve into a JSON object
+ * and calculate the total balance.
*
- * @param connection connection to the client
- * @param coin_pub public key of the coin
- * @param h_wire hash of wire details
- * @param h_contract_terms hash of proposal data
- * @param timestamp client'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 fee)
- * @return MHD result code
+ * @param rh reserve history to JSON-ify
+ * @param[out] balance set to current reserve balance
+ * @return json representation of the @a rh, NULL on error
*/
-int
-TEH_RESPONSE_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
refund_deadline,
- const struct TALER_MerchantPublicKeyP
*merchant,
- const struct TALER_Amount
*amount_without_fee);
+json_t *
+TEH_RESPONSE_compile_reserve_history (const struct
TALER_EXCHANGEDB_ReserveHistory *rh,
+ struct TALER_Amount *balance);
/**
@@ -290,46 +274,6 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct
MHD_Connection *connection,
/**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_TransactionList *tl);
-
-
-/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code. The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec error code uniquely identifying the error
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
- unsigned int response_code,
- enum TALER_ErrorCode ec);
-
-
-/**
- * Generate successful refund confirmation message.
- *
- * @param connection connection to the client
- * @param refund details about the successful refund
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Refund
*refund);
-
-
-/**
* A merchant asked for details about a deposit, but
* we do not know anything about the deposit. Generate the
* 404 reply.
@@ -344,273 +288,13 @@ TEH_RESPONSE_reply_transaction_unknown (struct
MHD_Connection *connection,
/**
- * A merchant asked for details about a deposit, but
- * we did not execute the deposit yet. Generate a 202 reply.
- *
- * @param connection connection to the client
- * @param planned_exec_time planned execution time
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection,
- struct GNUNET_TIME_Absolute
planned_exec_time);
-
-
-/**
- * A merchant asked for details about a deposit. Provide
- * them. Generates the 200 reply.
+ * Compile the transaction history of a coin into a JSON object.
*
- * @param connection connection to the client
- * @param h_contract_terms hash of the proposal data
- * @param h_wire hash of wire account details
- * @param coin_pub public key of the coin
- * @param coin_contribution contribution of this coin to the total amount
transferred
- * @param wtid raw wire transfer identifier
- * @param exec_time execution time of the wire transfer
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection,
- const struct GNUNET_HashCode
*h_contract_terms,
- const struct GNUNET_HashCode *h_wire,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub,
- const struct TALER_Amount
*coin_contribution,
- const struct
TALER_WireTransferIdentifierRawP *wtid,
- struct GNUNET_TIME_Absolute exec_time);
-
-
-/**
- * Detail for /wire/deposit response.
+ * @param tl transaction history to JSON-ify
+ * @return json representation of the @a rh
*/
-struct TEH_TrackTransferDetail
-{
-
- /**
- * We keep deposit details in a DLL.
- */
- struct TEH_TrackTransferDetail *next;
-
- /**
- * We keep deposit details in a DLL.
- */
- struct TEH_TrackTransferDetail *prev;
-
- /**
- * Hash of the proposal data.
- */
- struct GNUNET_HashCode h_contract_terms;
-
- /**
- * Coin's public key.
- */
- struct TALER_CoinSpendPublicKeyP coin_pub;
-
- /**
- * Total value of the coin.
- */
- struct TALER_Amount deposit_value;
-
- /**
- * Fees charged by the exchange for the deposit.
- */
- struct TALER_Amount deposit_fee;
-};
-
-
-/**
- * A merchant asked for transaction details about a wire transfer.
- * Provide them. Generates the 200 reply.
- *
- * @param connection connection to the client
- * @param total total amount that was transferred
- * @param merchant_pub public key of the merchant
- * @param h_wire destination account
- * @param wire_fee wire fee that was charged
- * @param exec_time execution time of the wire transfer
- * @param wdd_head linked list with details about the combined deposits
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
- const struct TALER_Amount *total,
- const struct
TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode
*h_wire,
- const struct TALER_Amount *wire_fee,
- struct GNUNET_TIME_Absolute
exec_time,
- const struct
TEH_TrackTransferDetail *wdd_head);
-
-
-/**
- * Send reserve status information to client.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_ReserveHistory *rh);
-
-
-/**
- * Send reserve status information to client with the
- * message that we have insufficient funds for the
- * requested /reserve/withdraw operation.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection
*connection,
- const struct
TALER_EXCHANGEDB_ReserveHistory *rh);
-
-
-/**
- * Send blinded coin information to client.
- *
- * @param connection connection to the client
- * @param collectable blinded coin to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
-
-
-/**
- * Send a confirmation response to a "/refresh/melt" request.
- *
- * @param connection the connection to send the response to
- * @param session_hash hash of the refresh session
- * @param noreveal_index which index will the client not have to reveal
- * @return a MHD status code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
- const struct GNUNET_HashCode
*session_hash,
- uint16_t noreveal_index);
-
-
-/**
- * Send a response for a failed "/refresh/melt" request. The
- * transaction history of the given coin demonstrates that the
- * @a residual value of the coin is below the @a requested
- * contribution of the coin for the melt. Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection
*connection,
- const struct
TALER_CoinSpendPublicKeyP *coin_pub,
- struct TALER_Amount
coin_value,
- struct
TALER_EXCHANGEDB_TransactionList *tl,
- struct TALER_Amount
requested,
- struct TALER_Amount
residual);
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
- unsigned int num_newcoins,
- const struct
TALER_DenominationSignature *sigs);
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param session info about session
- * @param commit_coins array of @a num_newcoins committed envelopes at offset
@a gamma
- * @param denom_pubs array of @a num_newcoins denomination keys for the new
coins
- * @param gamma_tp transfer public key at offset @a gamma
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
- const struct
TALER_EXCHANGEDB_RefreshSession *session,
- const struct
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
- const struct
TALER_DenominationPublicKey *denom_pubs,
- const struct
TALER_TransferPublicKeyP *gamma_tp);
-
-
-/**
- * @brief Information for each session a coin was melted into.
- */
-struct TEH_RESPONSE_LinkSessionInfo
-{
- /**
- * Transfer public key of the coin.
- */
- struct TALER_TransferPublicKeyP transfer_pub;
-
- /**
- * Linked data of coins being created in the session.
- */
- struct TALER_EXCHANGEDB_LinkDataList *ldl;
-
-};
-
-
-/**
- * Send a response for "/refresh/link".
- *
- * @param connection the connection to send the response to
- * @param num_sessions number of sessions the coin was used in
- * @param sessions array of @a num_session entries with
- * information for each session
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
- unsigned int num_sessions,
- const struct
TEH_RESPONSE_LinkSessionInfo *sessions);
-
-
-/**
- * A wallet asked for /payback, but we do not know anything about the
- * original withdraw operation specified. Generates a 404 reply.
- *
- * @param connection connection to the client
- * @param ec Taler error code
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection,
- enum TALER_ErrorCode ec);
-
-
-/**
- * A wallet asked for /payback, return the successful response.
- *
- * @param connection connection to the client
- * @param coin_pub coin for which we are processing the payback request
- * @param reserve_pub public key of the reserve that will receive the payback
- * @param amount the amount we will wire back
- * @param timestamp when did the exchange receive the /payback request
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub,
- const struct TALER_ReservePublicKeyP
*reserve_pub,
- const struct TALER_Amount *amount,
- struct GNUNET_TIME_Absolute timestamp);
+json_t *
+TEH_RESPONSE_compile_transaction_history (const struct
TALER_EXCHANGEDB_TransactionList *tl);
#endif
diff --git a/src/exchange/taler-exchange-httpd_track_transaction.c
b/src/exchange/taler-exchange-httpd_track_transaction.c
new file mode 100644
index 0000000..7c1bd6a
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_track_transaction.c
@@ -0,0 +1,368 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_track_transaction.c
+ * @brief Handle wire transfer tracking-related requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_signatures.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_track_transaction.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * A merchant asked for details about a deposit, but
+ * we did not execute the deposit yet. Generate a 202 reply.
+ *
+ * @param connection connection to the client
+ * @param planned_exec_time planned execution time
+ * @return MHD result code
+ */
+static int
+reply_transfer_pending (struct MHD_Connection *connection,
+ struct GNUNET_TIME_Absolute planned_exec_time)
+{
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_ACCEPTED,
+ "{s:o}",
+ "execution_time",
GNUNET_JSON_from_time_abs (planned_exec_time));
+}
+
+
+/**
+ * A merchant asked for details about a deposit. Provide
+ * them. Generates the 200 reply.
+ *
+ * @param connection connection to the client
+ * @param h_contract_terms hash of the contract
+ * @param h_wire hash of wire account details
+ * @param coin_pub public key of the coin
+ * @param coin_contribution how much did the coin we asked about
+ * contribute to the total transfer value? (deposit value minus fee)
+ * @param wtid raw wire transfer identifier
+ * @param exec_time execution time of the wire transfer
+ * @return MHD result code
+ */
+static int
+reply_track_transaction (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *h_contract_terms,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct GNUNET_TIME_Absolute exec_time)
+{
+ struct TALER_ConfirmWirePS cw;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+
+ cw.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE);
+ cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
+ cw.h_wire = *h_wire;
+ cw.h_contract_terms = *h_contract_terms;
+ cw.wtid = *wtid;
+ cw.coin_pub = *coin_pub;
+ cw.execution_time = GNUNET_TIME_absolute_hton (exec_time);
+ TALER_amount_hton (&cw.coin_contribution,
+ coin_contribution);
+ TEH_KS_sign (&cw.purpose,
+ &pub,
+ &sig);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o, s:o, s:o, s:o}",
+ "wtid", GNUNET_JSON_from_data_auto
(wtid),
+ "execution_time",
GNUNET_JSON_from_time_abs (exec_time),
+ "coin_contribution",
TALER_JSON_from_amount (coin_contribution),
+ "exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
+ "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #handle_wtid_data.
+ */
+struct DepositWtidContext
+{
+
+ /**
+ * Deposit details.
+ */
+ const struct TALER_DepositTrackPS *tps;
+
+ /**
+ * Public key of the merchant.
+ */
+ const struct TALER_MerchantPublicKeyP *merchant_pub;
+
+ /**
+ * Set by #handle_wtid data to the wire transfer ID.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Set by #handle_wtid data to the coin's contribution to the wire transfer.
+ */
+ struct TALER_Amount coin_contribution;
+
+ /**
+ * Set by #handle_wtid data to the fee charged to the coin.
+ */
+ struct TALER_Amount coin_fee;
+
+ /**
+ * Set by #handle_wtid data to the wire transfer execution time.
+ */
+ struct GNUNET_TIME_Absolute execution_time;
+
+ /**
+ * Set by #handle_wtid to the coin contribution to the transaction
+ * (that is, @e coin_contribution minus @e coin_fee).
+ */
+ struct TALER_Amount coin_delta;
+
+ /**
+ * Set to #GNUNET_YES by #handle_wtid if the wire transfer is still pending
+ * (and the above were not set).
+ * Set to #GNUNET_SYSERR if there was a serious error.
+ */
+ int pending;
+};
+
+
+/**
+ * Function called with the results of the lookup of the
+ * wire transfer identifier information.
+ *
+ * @param cls our context for transmission
+ * @param wtid raw wire transfer identifier, NULL
+ * if the transaction was not yet done
+ * @param coin_contribution how much did the coin we asked about
+ * contribute to the total transfer value? (deposit value including fee)
+ * @param coin_fee how much did the exchange charge for the deposit fee
+ * @param execution_time when was the transaction done, or
+ * when we expect it to be done (if @a wtid was NULL);
+ * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
+ * to the exchange
+ */
+static void
+handle_wtid_data (void *cls,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_Amount *coin_fee,
+ struct GNUNET_TIME_Absolute execution_time)
+{
+ struct DepositWtidContext *ctx = cls;
+
+ if (NULL == wtid)
+ {
+ ctx->pending = GNUNET_YES;
+ ctx->execution_time = execution_time;
+ return;
+ }
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&ctx->coin_delta,
+ coin_contribution,
+ coin_fee))
+ {
+ GNUNET_break (0);
+ ctx->pending = GNUNET_SYSERR;
+ return;
+ }
+ ctx->wtid = *wtid;
+ ctx->execution_time = execution_time;
+ ctx->coin_contribution = *coin_contribution;
+ ctx->coin_fee = *coin_fee;
+}
+
+
+/**
+ * Execute a "/track/transaction". Returns the transfer information
+ * associated with the given deposit.
+ *
+ * If it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure of type `struct DepositWtidContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+track_transaction_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct DepositWtidContext *ctx = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->wire_lookup_deposit_wtid (TEH_plugin->cls,
+ session,
+ &ctx->tps->h_contract_terms,
+ &ctx->tps->h_wire,
+ &ctx->tps->coin_pub,
+ ctx->merchant_pub,
+ &handle_wtid_data,
+ ctx);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED);
+ }
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_transaction_unknown (connection,
+
TALER_EC_TRACK_TRANSACTION_NOT_FOUND);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return qs;
+}
+
+
+/**
+ * Check the merchant signature, and if it is valid,
+ * return the wire transfer identifier.
+ *
+ * @param connection the MHD connection to handle
+ * @param tps signed request to execute
+ * @param merchant_pub public key from the merchant
+ * @param merchant_sig signature from the merchant (to be checked)
+ * @return MHD result code
+ */
+static int
+check_and_handle_track_transaction_request (struct MHD_Connection *connection,
+ const struct TALER_DepositTrackPS
*tps,
+ const struct
TALER_MerchantPublicKeyP *merchant_pub,
+ const struct
TALER_MerchantSignatureP *merchant_sig)
+{
+ struct DepositWtidContext ctx;
+ int mhd_ret;
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION,
+ &tps->purpose,
+ &merchant_sig->eddsa_sig,
+ &merchant_pub->eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ return TEH_RESPONSE_reply_signature_invalid (connection,
+
TALER_EC_TRACK_TRANSACTION_MERCHANT_SIGNATURE_INVALID,
+ "merchant_sig");
+ }
+ ctx.pending = GNUNET_NO;
+ ctx.tps = tps;
+ ctx.merchant_pub = merchant_pub;
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &track_transaction_transaction,
+ &ctx))
+ return mhd_ret;
+ if (GNUNET_YES == ctx.pending)
+ return reply_transfer_pending (connection,
+ ctx.execution_time);
+ if (GNUNET_SYSERR == ctx.pending)
+ return TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_TRACK_TRANSACTION_DB_FEE_INCONSISTENT);
+ return reply_track_transaction (connection,
+ &tps->h_contract_terms,
+ &tps->h_wire,
+ &tps->coin_pub,
+ &ctx.coin_delta,
+ &ctx.wtid,
+ ctx.execution_time);
+}
+
+
+/**
+ * Handle a "/track/transaction" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ int res;
+ json_t *json;
+ struct TALER_DepositTrackPS tps;
+ struct TALER_MerchantSignatureP merchant_sig;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("H_wire", &tps.h_wire),
+ GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &tps.h_contract_terms),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub", &tps.coin_pub),
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub", &tps.merchant),
+ GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &json);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == json) )
+ return MHD_YES;
+ res = TEH_PARSE_json_data (connection,
+ json,
+ spec);
+ if (GNUNET_OK != res)
+ {
+ json_decref (json);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ tps.purpose.size = htonl (sizeof (struct TALER_DepositTrackPS));
+ tps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION);
+ res = check_and_handle_track_transaction_request (connection,
+ &tps,
+ &tps.merchant,
+ &merchant_sig);
+ GNUNET_JSON_parse_free (spec);
+ json_decref (json);
+ return res;
+}
+
+
+/* end of taler-exchange-httpd_track_transaction.c */
diff --git a/src/exchange/taler-exchange-httpd_tracking.h
b/src/exchange/taler-exchange-httpd_track_transaction.h
similarity index 65%
copy from src/exchange/taler-exchange-httpd_tracking.h
copy to src/exchange/taler-exchange-httpd_track_transaction.h
index fe22304..f1cd366 100644
--- a/src/exchange/taler-exchange-httpd_tracking.h
+++ b/src/exchange/taler-exchange-httpd_track_transaction.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,12 +14,12 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_tracking.h
+ * @file taler-exchange-httpd_track_transaction.h
* @brief Handle wire transfer tracking-related requests
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_TRACKING_H
-#define TALER_EXCHANGE_HTTPD_TRACKING_H
+#ifndef TALER_EXCHANGE_HTTPD_TRACK_TRANSACTION_H
+#define TALER_EXCHANGE_HTTPD_TRACK_TRANSACTION_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
@@ -27,24 +27,6 @@
/**
- * Handle a "/track/transfer" request.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
-
-/**
* Handle a "/track/transaction" request.
*
* @param rh context of the handler
diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c
b/src/exchange/taler-exchange-httpd_track_transfer.c
new file mode 100644
index 0000000..57b621e
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_track_transfer.c
@@ -0,0 +1,486 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
+
+ You should have received a copy of the GNU Affero General Public License
along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_track_transfer.c
+ * @brief Handle wire transfer /track/transfer requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_signatures.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_track_transfer.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Detail for /wire/deposit response.
+ */
+struct TEH_TrackTransferDetail
+{
+
+ /**
+ * We keep deposit details in a DLL.
+ */
+ struct TEH_TrackTransferDetail *next;
+
+ /**
+ * We keep deposit details in a DLL.
+ */
+ struct TEH_TrackTransferDetail *prev;
+
+ /**
+ * Hash of the proposal data.
+ */
+ struct GNUNET_HashCode h_contract_terms;
+
+ /**
+ * Coin's public key.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Total value of the coin.
+ */
+ struct TALER_Amount deposit_value;
+
+ /**
+ * Fees charged by the exchange for the deposit.
+ */
+ struct TALER_Amount deposit_fee;
+};
+
+
+/**
+ * A merchant asked for transaction details about a wire transfer.
+ * Provide them. Generates the 200 reply.
+ *
+ * @param connection connection to the client
+ * @param total total amount that was transferred
+ * @param merchant_pub public key of the merchant
+ * @param h_wire destination account
+ * @param wire_fee wire fee that was charged
+ * @param exec_time execution time of the wire transfer
+ * @param wdd_head linked list with details about the combined deposits
+ * @return MHD result code
+ */
+static int
+reply_track_transfer_details (struct MHD_Connection *connection,
+ const struct TALER_Amount *total,
+ const struct TALER_MerchantPublicKeyP
*merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_Amount *wire_fee,
+ struct GNUNET_TIME_Absolute exec_time,
+ const struct TEH_TrackTransferDetail *wdd_head)
+{
+ const struct TEH_TrackTransferDetail *wdd_pos;
+ json_t *deposits;
+ struct TALER_WireDepositDetailP dd;
+ struct GNUNET_HashContext *hash_context;
+ struct TALER_WireDepositDataPS wdp;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+
+ GNUNET_TIME_round_abs (&exec_time);
+ deposits = json_array ();
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
+ for (wdd_pos = wdd_head; NULL != wdd_pos; wdd_pos = wdd_pos->next)
+ {
+ dd.h_contract_terms = wdd_pos->h_contract_terms;
+ dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
+ dd.coin_pub = wdd_pos->coin_pub;
+ TALER_amount_hton (&dd.deposit_value,
+ &wdd_pos->deposit_value);
+ TALER_amount_hton (&dd.deposit_fee,
+ &wdd_pos->deposit_fee);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &dd,
+ sizeof (struct TALER_WireDepositDetailP));
+ GNUNET_assert (0 ==
+ json_array_append_new (deposits,
+ json_pack ("{s:o, s:o, s:o, s:o}",
+ "h_contract_terms",
GNUNET_JSON_from_data_auto (&wdd_pos->h_contract_terms),
+ "coin_pub",
GNUNET_JSON_from_data_auto (&wdd_pos->coin_pub),
+ "deposit_value",
TALER_JSON_from_amount (&wdd_pos->deposit_value),
+ "deposit_fee",
TALER_JSON_from_amount (&wdd_pos->deposit_fee))));
+ }
+ wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
+ wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
+ TALER_amount_hton (&wdp.total,
+ total);
+ 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);
+ TEH_KS_sign (&wdp.purpose,
+ &pub,
+ &sig);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o, s:o, s:o, s:o, s:o, s:o,
s:o}",
+ "total", TALER_JSON_from_amount (total),
+ "wire_fee", TALER_JSON_from_amount
(wire_fee),
+ "merchant_pub",
GNUNET_JSON_from_data_auto (merchant_pub),
+ "H_wire", GNUNET_JSON_from_data_auto
(h_wire),
+ "execution_time",
GNUNET_JSON_from_time_abs (exec_time),
+ "deposits", deposits,
+ "exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
+ "exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #handle_transaction_data.
+ */
+struct WtidTransactionContext
+{
+
+ /**
+ * Identifier of the wire transfer to track.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Total amount of the wire transfer, as calculated by
+ * summing up the individual amounts. To be rounded down
+ * to calculate the real transfer amount at the end.
+ * Only valid if @e is_valid is #GNUNET_YES.
+ */
+ struct TALER_Amount total;
+
+ /**
+ * Public key of the merchant, only valid if @e is_valid
+ * is #GNUNET_YES.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Which method was used to wire the funds?
+ */
+ char *wire_method;
+
+ /**
+ * Hash of the wire details of the merchant (identical for all
+ * deposits), only valid if @e is_valid is #GNUNET_YES.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Wire fee applicable at @e exec_time.
+ */
+ struct TALER_Amount wire_fee;
+
+ /**
+ * Execution time of the wire transfer
+ */
+ struct GNUNET_TIME_Absolute exec_time;
+
+ /**
+ * Head of DLL with details for /wire/deposit response.
+ */
+ struct TEH_TrackTransferDetail *wdd_head;
+
+ /**
+ * Head of DLL with details for /wire/deposit response.
+ */
+ struct TEH_TrackTransferDetail *wdd_tail;
+
+ /**
+ * JSON array with details about the individual deposits.
+ */
+ json_t *deposits;
+
+ /**
+ * Initially #GNUNET_NO, if we found no deposits so far. Set to
+ * #GNUNET_YES if we got transaction data, and the database replies
+ * remained consistent with respect to @e merchant_pub and @e h_wire
+ * (as they should). Set to #GNUNET_SYSERR if we encountered an
+ * internal error.
+ */
+ int is_valid;
+
+};
+
+
+/**
+ * Function called with the results of the lookup of the
+ * transaction data for the given wire transfer identifier.
+ *
+ * @param cls our context for transmission
+ * @param rowid which row in the DB is the information from (for diagnostics)
+ * @param merchant_pub public key of the merchant (should be same for all
callbacks with the same @e cls)
+ * @param wire_method which wire plugin was used
+ * @param h_wire hash of wire transfer details of the merchant (should be same
for all callbacks with the same @e cls)
+ * @param exec_time execution time of the wire transfer (should be same for
all callbacks with the same @e cls)
+ * @param h_contract_terms which proposal was this payment about
+ * @param coin_pub which public key was this payment about
+ * @param deposit_value amount contributed by this coin in total
+ * @param deposit_fee deposit fee charged by exchange for this coin
+ */
+static void
+handle_transaction_data (void *cls,
+ uint64_t rowid,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const char *wire_method,
+ const struct GNUNET_HashCode *h_wire,
+ struct GNUNET_TIME_Absolute exec_time,
+ const struct GNUNET_HashCode *h_contract_terms,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *deposit_value,
+ const struct TALER_Amount *deposit_fee)
+{
+ struct WtidTransactionContext *ctx = cls;
+ struct TALER_Amount delta;
+ struct TEH_TrackTransferDetail *wdd;
+
+ if (GNUNET_SYSERR == ctx->is_valid)
+ return;
+ if (GNUNET_NO == ctx->is_valid)
+ {
+ ctx->merchant_pub = *merchant_pub;
+ ctx->h_wire = *h_wire;
+ ctx->exec_time = exec_time;
+ ctx->wire_method = GNUNET_strdup (wire_method);
+ ctx->is_valid = GNUNET_YES;
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&ctx->total,
+ deposit_value,
+ deposit_fee))
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ }
+ else
+ {
+ if ( (0 != memcmp (&ctx->merchant_pub,
+ merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) ||
+ (0 != strcmp (wire_method,
+ ctx->wire_method)) ||
+ (0 != memcmp (&ctx->h_wire,
+ h_wire,
+ sizeof (struct GNUNET_HashCode))) )
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&delta,
+ deposit_value,
+ deposit_fee))
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_add (&ctx->total,
+ &ctx->total,
+ &delta))
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ }
+ wdd = GNUNET_new (struct TEH_TrackTransferDetail);
+ wdd->deposit_value = *deposit_value;
+ wdd->deposit_fee = *deposit_fee;
+ wdd->h_contract_terms = *h_contract_terms;
+ wdd->coin_pub = *coin_pub;
+ GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
+ ctx->wdd_tail,
+ wdd);
+}
+
+
+/**
+ * Execute a "/track/transfer". Returns the transaction information
+ * associated with the given wire transfer identifier.
+ *
+ * If it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+track_transfer_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct WtidTransactionContext *ctx = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_TIME_Absolute wire_fee_start_date;
+ struct GNUNET_TIME_Absolute wire_fee_end_date;
+ struct TALER_MasterSignatureP wire_fee_master_sig;
+
+ ctx->is_valid = GNUNET_NO;
+ ctx->wdd_head = NULL;
+ ctx->wdd_tail = NULL;
+ ctx->wire_method = NULL;
+ qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
+ session,
+ &ctx->wtid,
+ &handle_transaction_data,
+ ctx);
+ if (0 > qs)
+ {
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
+ }
+ return qs;
+ }
+ if (GNUNET_SYSERR == ctx->is_valid)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (GNUNET_NO == ctx->is_valid)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
+
TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
+ "wtid");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
+ session,
+ ctx->wire_method,
+ ctx->exec_time,
+ &wire_fee_start_date,
+ &wire_fee_end_date,
+ &ctx->wire_fee,
+ &wire_fee_master_sig);
+ if (0 >= qs)
+ {
+ if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
+ (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS) )
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
+ }
+ return qs;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&ctx->total,
+ &ctx->total,
+ &ctx->wire_fee))
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Free data structure reachable from @a ctx, but not @a ctx itself.
+ *
+ * @param ctx context to free
+ */
+static void
+free_ctx (struct WtidTransactionContext *ctx)
+{
+ struct TEH_TrackTransferDetail *wdd;
+
+ while (NULL != (wdd = ctx->wdd_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
+ ctx->wdd_tail,
+ wdd);
+ GNUNET_free (wdd);
+ }
+ GNUNET_free_non_null (ctx->wire_method);
+}
+
+
+/**
+ * Handle a "/track/transfer" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct WtidTransactionContext ctx;
+ int res;
+ int mhd_ret;
+
+ memset (&ctx, 0, sizeof (ctx));
+ res = TEH_PARSE_mhd_request_arg_data (connection,
+ "wtid",
+ &ctx.wtid,
+ sizeof (struct
TALER_WireTransferIdentifierRawP));
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* internal error */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* parse error */
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &track_transfer_transaction,
+ &ctx))
+ {
+ free_ctx (&ctx);
+ return mhd_ret;
+ }
+ mhd_ret = reply_track_transfer_details (connection,
+ &ctx.total,
+ &ctx.merchant_pub,
+ &ctx.h_wire,
+ &ctx.wire_fee,
+ ctx.exec_time,
+ ctx.wdd_head);
+ free_ctx (&ctx);
+ return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_track_transfer.c */
diff --git a/src/exchange/taler-exchange-httpd_tracking.h
b/src/exchange/taler-exchange-httpd_track_transfer.h
similarity index 64%
rename from src/exchange/taler-exchange-httpd_tracking.h
rename to src/exchange/taler-exchange-httpd_track_transfer.h
index fe22304..2242b25 100644
--- a/src/exchange/taler-exchange-httpd_tracking.h
+++ b/src/exchange/taler-exchange-httpd_track_transfer.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -14,12 +14,12 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file taler-exchange-httpd_tracking.h
+ * @file taler-exchange-httpd_track_transfer.h
* @brief Handle wire transfer tracking-related requests
* @author Christian Grothoff
*/
-#ifndef TALER_EXCHANGE_HTTPD_TRACKING_H
-#define TALER_EXCHANGE_HTTPD_TRACKING_H
+#ifndef TALER_EXCHANGE_HTTPD_TRACK_TRANSFER_H
+#define TALER_EXCHANGE_HTTPD_TRACK_TRANSFER_H
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
@@ -43,23 +43,4 @@ TEH_TRACKING_handler_track_transfer (struct
TEH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size);
-
-/**
- * Handle a "/track/transaction" request.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size);
-
-
#endif
diff --git a/src/exchange/taler-exchange-httpd_tracking.c
b/src/exchange/taler-exchange-httpd_tracking.c
deleted file mode 100644
index a973549..0000000
--- a/src/exchange/taler-exchange-httpd_tracking.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free
Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
-
- You should have received a copy of the GNU Affero General Public License
along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_tracking.c
- * @brief Handle wire transfer tracking-related requests
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <pthread.h>
-#include "taler_signatures.h"
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_tracking.h"
-#include "taler-exchange-httpd_responses.h"
-
-
-/**
- * Handle a "/track/transfer" request.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- struct TALER_WireTransferIdentifierRawP wtid;
- int res;
-
- res = TEH_PARSE_mhd_request_arg_data (connection,
- "wtid",
- &wtid,
- sizeof (struct
TALER_WireTransferIdentifierRawP));
- if (GNUNET_SYSERR == res)
- return MHD_NO; /* internal error */
- if (GNUNET_NO == res)
- return MHD_YES; /* parse error */
- return TEH_DB_execute_track_transfer (connection,
- &wtid);
-}
-
-
-/**
- * Check the merchant signature, and if it is valid,
- * return the wire transfer identifier.
- *
- * @param connection the MHD connection to handle
- * @param tps signed request to execute
- * @param merchant_pub public key from the merchant
- * @param merchant_sig signature from the merchant (to be checked)
- * @return MHD result code
- */
-static int
-check_and_handle_track_transaction_request (struct MHD_Connection *connection,
- const struct TALER_DepositTrackPS
*tps,
- struct TALER_MerchantPublicKeyP
*merchant_pub,
- struct TALER_MerchantSignatureP
*merchant_sig)
-{
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION,
- &tps->purpose,
- &merchant_sig->eddsa_sig,
- &merchant_pub->eddsa_pub))
- {
- GNUNET_break_op (0);
- return TEH_RESPONSE_reply_signature_invalid (connection,
-
TALER_EC_TRACK_TRANSACTION_MERCHANT_SIGNATURE_INVALID,
- "merchant_sig");
- }
- return TEH_DB_execute_track_transaction (connection,
- &tps->h_contract_terms,
- &tps->h_wire,
- &tps->coin_pub,
- merchant_pub);
-}
-
-
-/**
- * Handle a "/track/transaction" request.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- int res;
- json_t *json;
- struct TALER_DepositTrackPS tps;
- struct TALER_MerchantSignatureP merchant_sig;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("H_wire", &tps.h_wire),
- GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &tps.h_contract_terms),
- GNUNET_JSON_spec_fixed_auto ("coin_pub", &tps.coin_pub),
- GNUNET_JSON_spec_fixed_auto ("merchant_pub", &tps.merchant),
- GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &json);
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- if ( (GNUNET_NO == res) || (NULL == json) )
- return MHD_YES;
- res = TEH_PARSE_json_data (connection,
- json,
- spec);
- if (GNUNET_OK != res)
- {
- json_decref (json);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
- tps.purpose.size = htonl (sizeof (struct TALER_DepositTrackPS));
- tps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION);
- res = check_and_handle_track_transaction_request (connection,
- &tps,
- &tps.merchant,
- &merchant_sig);
- GNUNET_JSON_parse_free (spec);
- json_decref (json);
- return res;
-}
-
-
-/* end of taler-exchange-httpd_tracking.c */
diff --git a/src/exchange/taler-exchange-wirewatch.c
b/src/exchange/taler-exchange-wirewatch.c
index aee3083..312f8ac 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -95,6 +95,11 @@ static char *type;
static int delay;
/**
+ * Are we run in testing mode and should only do one pass?
+ */
+static int test_mode;
+
+/**
* Next task to run, if any.
*/
static struct GNUNET_SCHEDULER_Task *task;
@@ -218,22 +223,28 @@ history_cb (void *cls,
const struct TALER_WIRE_TransferDetails *details)
{
struct TALER_EXCHANGEDB_Session *session = cls;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
if (TALER_BANK_DIRECTION_NONE == dir)
{
hh = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"End of list. Committing progress!\n");
- ret = db_plugin->commit (db_plugin->cls,
- session);
- if (GNUNET_OK == ret)
+ qs = db_plugin->commit (db_plugin->cls,
+ session);
+ if (0 <= qs)
{
GNUNET_free_non_null (start_off);
start_off = last_row_off;
start_off_size = last_row_off_size;
}
+ if ( (GNUNET_YES == delay) &&
+ (test_mode) )
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_OK;
+ }
if (GNUNET_YES == delay)
task = GNUNET_SCHEDULER_add_delayed (DELAY,
&find_transfers,
@@ -243,22 +254,29 @@ history_cb (void *cls,
NULL);
return GNUNET_OK; /* will be ignored anyway */
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Adding wire transfer over %s with subject `%s'\n",
TALER_amount2s (&details->amount),
TALER_B2S (&details->reserve_pub));
- ret = db_plugin->reserves_in_insert (db_plugin->cls,
- session,
- &details->reserve_pub,
- &details->amount,
- details->execution_date,
- details->account_details,
- row_off,
- row_off_size);
- if (GNUNET_OK != ret)
+ qs = db_plugin->reserves_in_insert (db_plugin->cls,
+ session,
+ &details->reserve_pub,
+ &details->amount,
+ details->execution_date,
+ details->account_details,
+ row_off,
+ row_off_size);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
db_plugin->rollback (db_plugin->cls,
+ session);
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ db_plugin->rollback (db_plugin->cls,
session);
/* try again */
task = GNUNET_SCHEDULER_add_now (&find_transfers,
@@ -287,9 +305,10 @@ static void
find_transfers (void *cls)
{
struct TALER_EXCHANGEDB_Session *session;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ task = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking for incoming wire transfers\n");
if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
@@ -310,11 +329,11 @@ find_transfers (void *cls)
GNUNET_SCHEDULER_shutdown ();
return;
}
- ret = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
- session,
- &start_off,
- &start_off_size);
- if (GNUNET_SYSERR == ret)
+ qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
+ session,
+ &start_off,
+ &start_off_size);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to obtain starting point for montoring from
database!\n");
@@ -322,6 +341,15 @@ find_transfers (void *cls)
GNUNET_SCHEDULER_shutdown ();
return;
}
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ /* try again */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ task = GNUNET_SCHEDULER_add_now (&find_transfers,
+ NULL);
+ return;
+ }
delay = GNUNET_YES;
hh = wire_plugin->get_history (wire_plugin->cls,
TALER_BANK_DIRECTION_CREDIT,
@@ -389,7 +417,10 @@ main (int argc,
"PLUGINNAME",
"which wire plugin to use",
&type),
- GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_option_flag ('T',
+ "test",
+ "run in test mode and exit when idle",
+ &test_mode),
GNUNET_GETOPT_OPTION_END
};
diff --git a/src/exchange/test-taler-exchange-aggregator-postgres.conf
b/src/exchange/test-taler-exchange-wirewatch-postgres.conf
similarity index 94%
copy from src/exchange/test-taler-exchange-aggregator-postgres.conf
copy to src/exchange/test-taler-exchange-wirewatch-postgres.conf
index f609c0a..2e846d8 100644
--- a/src/exchange/test-taler-exchange-aggregator-postgres.conf
+++ b/src/exchange/test-taler-exchange-wirewatch-postgres.conf
@@ -26,7 +26,9 @@ BASE_URL = "https://exchange.taler.net/"
# as there is no way for wallets to query this value. Thus,
# it is only configurable for testing, and should be treated
# as constant in production.
-IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+#
+# This is THE test that requires a short reserve expiration time!
+IDLE_RESERVE_EXPIRATION_TIME = 5 s
[exchangedb-postgres]
diff --git a/src/exchange/test_taler_exchange_aggregator.c
b/src/exchange/test_taler_exchange_aggregator.c
index 57cb9d3..7f9ea41 100644
--- a/src/exchange/test_taler_exchange_aggregator.c
+++ b/src/exchange/test_taler_exchange_aggregator.c
@@ -118,7 +118,7 @@ struct Command
/**
* Subject of the transfer, set by the command.
*/
- struct TALER_WireTransferIdentifierRawP wtid;
+ char *subject;
} expect_transaction;
@@ -450,7 +450,7 @@ do_deposit (struct Command *cmd)
plugin->insert_deposit (plugin->cls,
session,
&deposit)) ||
- (GNUNET_OK !=
+ (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls,
session)) )
ret = GNUNET_SYSERR;
@@ -565,7 +565,7 @@ interpreter (void *cls)
cmd->details.expect_transaction.debit_account,
cmd->details.expect_transaction.credit_account,
cmd->details.expect_transaction.exchange_base_url,
- &cmd->details.expect_transaction.wtid))
+ &cmd->details.expect_transaction.subject))
{
fail (cmd);
return;
@@ -1143,12 +1143,12 @@ run (void *cls)
if ( (GNUNET_OK !=
plugin->start (plugin->cls,
session)) ||
- (GNUNET_OK !=
+ (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_denomination_info (plugin->cls,
session,
&dpk,
&issue)) ||
- (GNUNET_OK !=
+ (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls,
session)) )
{
diff --git a/src/exchange/test_taler_exchange_wirewatch.c
b/src/exchange/test_taler_exchange_wirewatch.c
new file mode 100644
index 0000000..69502d9
--- /dev/null
+++ b/src/exchange/test_taler_exchange_wirewatch.c
@@ -0,0 +1,848 @@
+/*
+ This file is part of TALER
+ (C) 2016, 2017 Inria and GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/test_taler_exchange_wirewatch.c
+ * @brief Tests for taler-exchange-wirewatch and taler-exchange-aggregator
logic;
+ * Performs an invalid wire transfer to the exchange, and then checks
that
+ * wirewatch immediately sends the money back.
+ * Then performs a valid wire transfer, waits for the reserve to expire,
+ * and then checks that the aggregator sends the money back.
+ * @author Christian Grothoff <address@hidden>
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_json_lib.h>
+#include "taler_json_lib.h"
+#include <microhttpd.h>
+#include "taler_fakebank_lib.h"
+
+
+/**
+ * Commands for the interpreter.
+ */
+enum OpCode {
+
+ /**
+ * Terminate testcase with 'skipped' result.
+ */
+ OPCODE_TERMINATE_SKIP,
+
+ /**
+ * Run taler-exchange-aggregator.
+ */
+ OPCODE_RUN_AGGREGATOR,
+
+ /**
+ * Expect that we have exhaustively gone over all transactions.
+ */
+ OPCODE_RUN_WIREWATCH,
+
+ /**
+ * Send money from bank to exchange.
+ */
+ OPCODE_RUN_TRANSFER,
+
+ /**
+ * Wait a certain amount of time.
+ */
+ OPCODE_WAIT,
+
+ /**
+ * Expect that we have received the specified transfer.
+ */
+ OPCODE_EXPECT_TRANSFER,
+
+ /**
+ * Expect that we have 'expected' all wire transfers.
+ */
+ OPCODE_EXPECT_TRANSFERS_EMPTY,
+
+ /**
+ * Finish testcase with success.
+ */
+ OPCODE_TERMINATE_SUCCESS
+};
+
+
+/**
+ * Command state for the interpreter.
+ */
+struct Command
+{
+
+ /**
+ * What instruction should we run?
+ */
+ enum OpCode opcode;
+
+ /**
+ * Human-readable label for the command.
+ */
+ const char *label;
+
+ union {
+
+ /**
+ * If @e opcode is #OPCODE_EXPECT_TRANSFER, this
+ * specifies which transaction we expected. Note that
+ * the WTID will be set, not checked!
+ */
+ struct {
+
+ /**
+ * Amount to be transferred.
+ */
+ const char *amount;
+
+ /**
+ * Account to debit.
+ */
+ uint64_t debit_account;
+
+ /**
+ * Account to credit.
+ */
+ uint64_t credit_account;
+
+ /**
+ * Expected base URL for the exchange.
+ */
+ const char *exchange_base_url;
+
+ /**
+ * Subject of the transfer, set by the command.
+ */
+ char *subject;
+
+ } expect_transfer;
+
+
+ /**
+ * If @e opcode is #OPCODE_RUN_TRANSFER, this
+ * specifies which transaction the bank should do.
+ */
+ struct {
+
+ /**
+ * Amount to be transferred.
+ */
+ const char *amount;
+
+ /**
+ * Account to debit.
+ */
+ uint64_t debit_account;
+
+ /**
+ * Account to credit.
+ */
+ uint64_t credit_account;
+
+ /**
+ * Subject of the transfer, set by the command.
+ */
+ const char *subject;
+
+ /**
+ * Serial ID of the wire transfer as assigned by the bank.
+ */
+ uint64_t serial_id;
+
+ } run_transfer;
+
+ struct {
+
+ /**
+ * The handle for the aggregator process that we are testing.
+ */
+ struct GNUNET_OS_Process *aggregator_proc;
+
+ /**
+ * ID of task called whenever we get a SIGCHILD.
+ */
+ struct GNUNET_SCHEDULER_Task *child_death_task;
+
+ } aggregator;
+
+ struct {
+
+ /**
+ * The handle for the wirewatch process that we are testing.
+ */
+ struct GNUNET_OS_Process *wirewatch_proc;
+
+ /**
+ * ID of task called whenever we get a SIGCHILD.
+ */
+ struct GNUNET_SCHEDULER_Task *child_death_task;
+
+ } wirewatch;
+
+ /**
+ * How long should we wait if the opcode is #OPCODE_WAIT.
+ */
+ struct GNUNET_TIME_Relative wait_delay;
+
+ } details;
+
+};
+
+
+/**
+ * State of the interpreter.
+ */
+struct State
+{
+ /**
+ * Array of commands to run.
+ */
+ struct Command* commands;
+
+ /**
+ * Offset of the next command to be run.
+ */
+ unsigned int ioff;
+};
+
+
+/**
+ * Pipe used to communicate child death via signal.
+ */
+static struct GNUNET_DISK_PipeHandle *sigpipe;
+
+/**
+ * ID of task called whenever we time out.
+ */
+static struct GNUNET_SCHEDULER_Task *timeout_task;
+
+/**
+ * Return value from main().
+ */
+static int result;
+
+/**
+ * Name of the configuration file to use.
+ */
+static char *config_filename;
+
+/**
+ * Task running the interpreter().
+ */
+static struct GNUNET_SCHEDULER_Task *int_task;
+
+/**
+ * Handle for our fake bank.
+ */
+static struct TALER_FAKEBANK_Handle *fb;
+
+
+/**
+ * Interprets the commands from the test program.
+ *
+ * @param cls the `struct State` of the interpreter
+ */
+static void
+interpreter (void *cls);
+
+
+/**
+ * Advance the IP and run the next command.
+ *
+ * @param state interpreter to advance.
+ */
+static void
+next_command (struct State *state)
+{
+ GNUNET_assert (NULL == int_task);
+ state->ioff++;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Advancing to command %s\n",
+ state->commands[state->ioff].label);
+ int_task = GNUNET_SCHEDULER_add_now (&interpreter,
+ state);
+}
+
+
+/**
+ * Fail the testcase at the current command.
+ */
+static void
+fail (struct Command *cmd)
+{
+ GNUNET_assert (NULL == int_task);
+ fprintf (stderr,
+ "Testcase failed at command `%s'\n",
+ cmd->label);
+ result = 2;
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Task triggered whenever we are to shutdown.
+ *
+ * @param cls closure, NULL if we need to self-restart
+ */
+static void
+timeout_action (void *cls)
+{
+ timeout_task = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Test failed: timeout\n");
+ result = 2;
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Task triggered whenever we are to shutdown.
+ *
+ * @param cls our `struct State`
+ */
+static void
+shutdown_action (void *cls)
+{
+ struct State *state = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Running shutdown\n");
+ if (NULL != timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (timeout_task);
+ timeout_task = NULL;
+ }
+ if (NULL != int_task)
+ {
+ GNUNET_SCHEDULER_cancel (int_task);
+ int_task = NULL;
+ }
+ if (NULL != fb)
+ {
+ TALER_FAKEBANK_stop (fb);
+ fb = NULL;
+ }
+ for (unsigned int i=0;i<=state->ioff;i++)
+ {
+ struct Command *cmd = &state->commands[i];
+
+ switch (cmd->opcode)
+ {
+ case OPCODE_TERMINATE_SKIP:
+ break;
+ case OPCODE_RUN_AGGREGATOR:
+ if (NULL != cmd->details.aggregator.child_death_task)
+ {
+ GNUNET_SCHEDULER_cancel (cmd->details.aggregator.child_death_task);
+ cmd->details.aggregator.child_death_task = NULL;
+ }
+ if (NULL != cmd->details.aggregator.aggregator_proc)
+ {
+ GNUNET_break (0 == GNUNET_OS_process_kill
(cmd->details.aggregator.aggregator_proc,
+ SIGKILL));
+ GNUNET_OS_process_wait (cmd->details.aggregator.aggregator_proc);
+ GNUNET_OS_process_destroy (cmd->details.aggregator.aggregator_proc);
+ cmd->details.aggregator.aggregator_proc = NULL;
+ }
+ break;
+ case OPCODE_RUN_WIREWATCH:
+ if (NULL != cmd->details.wirewatch.child_death_task)
+ {
+ GNUNET_SCHEDULER_cancel (cmd->details.wirewatch.child_death_task);
+ cmd->details.wirewatch.child_death_task = NULL;
+ }
+ if (NULL != cmd->details.wirewatch.wirewatch_proc)
+ {
+ GNUNET_break (0 == GNUNET_OS_process_kill
(cmd->details.wirewatch.wirewatch_proc,
+ SIGKILL));
+ GNUNET_OS_process_wait (cmd->details.wirewatch.wirewatch_proc);
+ GNUNET_OS_process_destroy (cmd->details.wirewatch.wirewatch_proc);
+ cmd->details.wirewatch.wirewatch_proc = NULL;
+ }
+ break;
+ case OPCODE_RUN_TRANSFER:
+ break;
+ case OPCODE_WAIT:
+ break;
+ case OPCODE_EXPECT_TRANSFER:
+ GNUNET_free_non_null (cmd->details.expect_transfer.subject);
+ cmd->details.expect_transfer.subject = NULL;
+ break;
+ case OPCODE_EXPECT_TRANSFERS_EMPTY:
+ break;
+ case OPCODE_TERMINATE_SUCCESS:
+ break;
+ }
+ }
+}
+
+
+/**
+ * Task triggered whenever we receive a SIGCHLD (child
+ * process died).
+ *
+ * @param cls our `struct State`
+ */
+static void
+maint_child_death (void *cls)
+{
+ struct State *state = cls;
+ const struct GNUNET_DISK_FileHandle *pr;
+ struct Command *cmd = &state->commands[state->ioff];
+ char c[16];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Child process died for command %s\n",
+ cmd->label);
+ pr = GNUNET_DISK_pipe_handle (sigpipe,
+ GNUNET_DISK_PIPE_END_READ);
+ GNUNET_break (0 < GNUNET_DISK_file_read (pr,
+ &c,
+ sizeof (c)));
+ switch (cmd->opcode)
+ {
+ case OPCODE_RUN_AGGREGATOR:
+ GNUNET_assert (NULL != cmd->details.aggregator.child_death_task);
+ cmd->details.aggregator.child_death_task = NULL;
+ GNUNET_OS_process_wait (cmd->details.aggregator.aggregator_proc);
+ GNUNET_OS_process_destroy (cmd->details.aggregator.aggregator_proc);
+ cmd->details.aggregator.aggregator_proc = NULL;
+ break;
+ case OPCODE_RUN_WIREWATCH:
+ GNUNET_assert (NULL != cmd->details.wirewatch.child_death_task);
+ cmd->details.wirewatch.child_death_task = NULL;
+ GNUNET_OS_process_wait (cmd->details.wirewatch.wirewatch_proc);
+ GNUNET_OS_process_destroy (cmd->details.wirewatch.wirewatch_proc);
+ cmd->details.wirewatch.wirewatch_proc = NULL;
+ break;
+ default:
+ fail (cmd);
+ return;
+ }
+ next_command (state);
+}
+
+
+/**
+ * Interprets the commands from the test program.
+ *
+ * @param cls the `struct State` of the interpreter
+ */
+static void
+interpreter (void *cls)
+{
+ struct State *state = cls;
+ struct Command *cmd = &state->commands[state->ioff];
+
+ GNUNET_assert (NULL != int_task);
+ int_task = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Running command %u (%s)\n",
+ state->ioff,
+ cmd->label);
+ switch (cmd->opcode)
+ {
+ case OPCODE_TERMINATE_SKIP:
+ /* return skip: test not finished, but did not fail either */
+ result = 77;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ case OPCODE_RUN_AGGREGATOR:
+ cmd->details.aggregator.child_death_task =
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_DISK_pipe_handle (sigpipe,
+
GNUNET_DISK_PIPE_END_READ),
+ &maint_child_death,
+ state);
+ cmd->details.aggregator.aggregator_proc
+ = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-aggregator",
+ "taler-exchange-aggregator",
+ "-c", config_filename,
+ "-t", /* enable temporary tables */
+ NULL);
+ if (NULL == cmd->details.aggregator.aggregator_proc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to start taler-exchange-aggregator. Check $PATH.\n");
+ GNUNET_break (0);
+ fail (cmd);
+ return;
+ }
+ return;
+ case OPCODE_RUN_WIREWATCH:
+ cmd->details.wirewatch.child_death_task =
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_DISK_pipe_handle (sigpipe,
+
GNUNET_DISK_PIPE_END_READ),
+ &maint_child_death,
+ state);
+ cmd->details.wirewatch.wirewatch_proc
+ = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-wirewatch",
+ "taler-exchange-wirewatch",
+ "-c", config_filename,
+ "-t", "test",
+ "-T", /* run in test mode, exit instead of
looping */
+ NULL);
+ if (NULL == cmd->details.wirewatch.wirewatch_proc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to start taler-exchange-wirewatch. Check $PATH.\n");
+ GNUNET_break (0);
+ fail (cmd);
+ return;
+ }
+ return;
+ case OPCODE_RUN_TRANSFER:
+ {
+ struct TALER_Amount amount;
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (cmd->details.run_transfer.amount,
+ &amount))
+ {
+ GNUNET_break (0);
+ fail (cmd);
+ return;
+ }
+ GNUNET_assert (NULL != cmd->details.run_transfer.subject);
+ cmd->details.run_transfer.serial_id
+ = TALER_FAKEBANK_make_transfer (fb,
+
cmd->details.run_transfer.debit_account,
+
cmd->details.run_transfer.credit_account,
+ &amount,
+ cmd->details.run_transfer.subject,
+ "https://exchange.taler.net/");
+ next_command (state);
+ return;
+ }
+ case OPCODE_WAIT:
+ state->ioff++;
+ GNUNET_assert (NULL == int_task);
+ int_task = GNUNET_SCHEDULER_add_delayed (cmd->details.wait_delay,
+ &interpreter,
+ state);
+ return;
+ case OPCODE_EXPECT_TRANSFER:
+ {
+ struct TALER_Amount want_amount;
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (cmd->details.expect_transfer.amount,
+ &want_amount))
+ {
+ GNUNET_break (0);
+ fail (cmd);
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_FAKEBANK_check (fb,
+ &want_amount,
+ cmd->details.expect_transfer.debit_account,
+ cmd->details.expect_transfer.credit_account,
+ cmd->details.expect_transfer.exchange_base_url,
+ &cmd->details.expect_transfer.subject))
+ {
+ fail (cmd);
+ return;
+ }
+ next_command (state);
+ return;
+ }
+ case OPCODE_EXPECT_TRANSFERS_EMPTY:
+ if (GNUNET_OK != TALER_FAKEBANK_check_empty (fb))
+ {
+ fail (cmd);
+ return;
+ }
+ next_command (state);
+ return;
+ case OPCODE_TERMINATE_SUCCESS:
+ result = 0;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with configuration
+ */
+static void
+run (void *cls)
+{
+ static struct Command commands[] = {
+ /* test running with empty DB */
+ {
+ .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+ .label = "expect-empty-transactions-on-start"
+ },
+ {
+ .opcode = OPCODE_RUN_AGGREGATOR,
+ .label = "run-aggregator-on-empty"
+ },
+ {
+ .opcode = OPCODE_RUN_WIREWATCH,
+ .label = "run-wirewatch-on-empty"
+ },
+ {
+ .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+ .label = "expect-empty-transactions-after-dry-run"
+ },
+ /* fill exchange's reserve at bank */
+ {
+ .opcode = OPCODE_RUN_TRANSFER,
+ .label = "run-transfer-good-to-exchange",
+ .details.run_transfer.debit_account = 4,
+ .details.run_transfer.credit_account = 3,
+ .details.run_transfer.subject =
"SRB8VQHNTNJWSSG7BXT24Z063ZSXN7T0MHCQCBAFC1V17BZH10D0",
+ .details.run_transfer.amount = "EUR:5.00"
+ },
+ /* creates reserve */
+ {
+ .opcode = OPCODE_RUN_WIREWATCH,
+ .label = "run-wirewatch-on-good-transfer"
+ },
+ /* clear first transfer from DLL */
+ {
+ .opcode = OPCODE_EXPECT_TRANSFER,
+ .label = "clear-good-transfer-to-exchange",
+ .details.expect_transfer.debit_account = 4,
+ .details.expect_transfer.credit_account = 3,
+ .details.expect_transfer.exchange_base_url =
"https://exchange.taler.net/",
+ .details.expect_transfer.amount = "EUR:5.00"
+ },
+ /* should do NOTHING, it is too early... */
+ {
+ .opcode = OPCODE_RUN_AGGREGATOR,
+ .label = "run-aggregator-non-expired-reserve"
+ },
+ /* check nothing happened */
+ {
+ .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+ .label = "expect-empty-transactions-1"
+ },
+ /* Configuration says reserves expire after 5s! */
+ {
+ .opcode = OPCODE_WAIT,
+ .label = "wait (5s)",
+ .details.wait_delay = { 1000LL * 1000 * 6 } /* 6s */
+ },
+ /* This time the reserve expired, so the money should go back... */
+ {
+ .opcode = OPCODE_RUN_AGGREGATOR,
+ .label = "run-aggregator-non-expired-reserve"
+ },
+ /* Check exchange sent money back, minus closing fee of EUR:0.01 */
+ {
+ .opcode = OPCODE_EXPECT_TRANSFER,
+ .label = "check-reserve-expiration-transfer",
+ .details.expect_transfer.debit_account = 3,
+ .details.expect_transfer.credit_account = 4,
+ .details.expect_transfer.exchange_base_url =
"https://exchange.taler.net/",
+ .details.expect_transfer.amount = "EUR:4.99"
+ },
+ /* check nothing else happened */
+ {
+ .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+ .label = "expect-empty-transactions-1"
+ },
+ /* This cannot work unless #5077 is implemented. */
+#if TEST_5077
+ {
+ .opcode = OPCODE_RUN_TRANSFER,
+ .label = "run-transfer-bad-to-exchange",
+ .details.run_transfer.debit_account = 4,
+ .details.run_transfer.credit_account = 3,
+ .details.run_transfer.subject = "random junk",
+ .details.run_transfer.amount = "EUR:5.00"
+ },
+ {
+ .opcode = OPCODE_RUN_WIREWATCH,
+ .label = "run-wirewatch-on-bad-transfer"
+ },
+ {
+ .opcode = OPCODE_EXPECT_TRANSFER,
+ .label = "expect-bad-transfer-to-exchange",
+ .details.expect_transfer.debit_account = 4,
+ .details.expect_transfer.credit_account = 3,
+ .details.expect_transfer.exchange_base_url =
"https://exchange.taler.net/",
+ .details.expect_transfer.amount = "EUR:5.00"
+ },
+ {
+ .opcode = OPCODE_EXPECT_TRANSFER,
+ .label = "expect-rewire-transfer-from-exchange",
+ .details.expect_transfer.debit_account = 3,
+ .details.expect_transfer.credit_account = 4,
+ .details.expect_transfer.exchange_base_url =
"https://exchange.taler.net/",
+ .details.expect_transfer.amount = "EUR:5.00"
+ },
+ {
+ .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+ .label = "expect-empty-transactions-1"
+ },
+#endif
+
+ {
+ .opcode = OPCODE_TERMINATE_SUCCESS,
+ .label = "testcase-complete-terminating-with-success"
+ }
+ };
+ static struct State state = {
+ .commands = commands
+ };
+
+ GNUNET_SCHEDULER_add_shutdown (&shutdown_action,
+ &state);
+ timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
+ &timeout_action,
+ &state);
+ result = 1; /* test failed for undefined reason */
+ fb = TALER_FAKEBANK_start (8082);
+ if (NULL == fb)
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ result = 77;
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Launching interpreter\n");
+ int_task = GNUNET_SCHEDULER_add_now (&interpreter,
+ &state);
+}
+
+
+/**
+ * Signal handler called for SIGCHLD. Triggers the
+ * respective handler by writing to the trigger pipe.
+ */
+static void
+sighandler_child_death ()
+{
+ static char c;
+ int old_errno = errno; /* back-up errno */
+
+ GNUNET_break (1 ==
+ GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
+ (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
+ &c, sizeof (c)));
+ errno = old_errno; /* restore errno */
+}
+
+
+int
+main (int argc,
+ char *const argv[])
+{
+ const char *plugin_name;
+ char *testname;
+ struct GNUNET_OS_Process *proc;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct GNUNET_SIGNAL_Context *shc_chld;
+
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+ plugin_name++;
+ (void) GNUNET_asprintf (&testname,
+ "test-taler-exchange-wirewatch-%s",
+ plugin_name);
+ (void) GNUNET_asprintf (&config_filename,
+ "%s.conf",
+ testname);
+ /* these might get in the way */
+ unsetenv ("XDG_DATA_HOME");
+ unsetenv ("XDG_CONFIG_HOME");
+ GNUNET_log_setup ("test_taler_exchange_wirewatch",
+ "WARNING",
+ NULL);
+ proc = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-keyup",
+ "taler-exchange-keyup",
+ "-c", config_filename,
+ NULL);
+ if (NULL == proc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to run `taler-exchange-keyup`, is your PATH
correct?\n");
+ return 77;
+ }
+ GNUNET_OS_process_wait (proc);
+ GNUNET_OS_process_destroy (proc);
+ proc = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-dbinit",
+ "taler-exchange-dbinit",
+ "-c", config_filename,
+ "-r",
+ NULL);
+ if (NULL == proc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to run `taler-exchange-keyup`, is your PATH
correct?\n");
+ return 77;
+ }
+ GNUNET_OS_process_wait (proc);
+ GNUNET_OS_process_destroy (proc);
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
+ 8082))
+ {
+ fprintf (stderr,
+ "Required port %u not available, skipping.\n",
+ 8082);
+ return 77;
+ }
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse (cfg,
+ config_filename))
+ {
+ GNUNET_break (0);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return 2;
+ }
+ sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
+ GNUNET_NO, GNUNET_NO);
+ GNUNET_assert (NULL != sigpipe);
+ shc_chld =
+ GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
+ &sighandler_child_death);
+ GNUNET_SCHEDULER_run (&run,
+ cfg);
+ GNUNET_SIGNAL_handler_uninstall (shc_chld);
+ GNUNET_DISK_pipe_close (sigpipe);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return result;
+}
+
+/* end of test_taler_exchange_wirewatch.c */
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c
b/src/exchangedb/perf_taler_exchangedb_interpreter.c
index 5e4155c..7ec958c 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
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
@@ -1292,13 +1292,16 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
break;
case PERF_TALER_EXCHANGEDB_CMD_START_TRANSACTION:
- state->plugin->start (state->plugin->cls, state->session);
+ GNUNET_break (GNUNET_OK ==
+ state->plugin->start (state->plugin->cls,
+ state->session));
break;
case PERF_TALER_EXCHANGEDB_CMD_COMMIT_TRANSACTION:
- state->plugin->commit (state->plugin->cls, state->session);
+ GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
+ state->plugin->commit (state->plugin->cls,
+ state->session));
break;
-
case PERF_TALER_EXCHANGEDB_CMD_ABORT_TRANSACTION:
state->plugin->rollback (state->plugin->cls,
state->session);
@@ -1331,15 +1334,15 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_INSERT_DEPOSIT:
{
int deposit_index;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
struct TALER_EXCHANGEDB_Deposit *deposit;
deposit_index =
state->cmd[state->i].details.insert_deposit.index_deposit;
deposit = state->cmd[deposit_index].exposed.data.deposit;
- ret = state->plugin->insert_deposit (state->plugin->cls,
+ qs = state->plugin->insert_deposit (state->plugin->cls,
state->session,
deposit);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
state->cmd[state->i].exposed.data.deposit = deposit;
}
break;
@@ -1347,7 +1350,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state
*state)
case PERF_TALER_EXCHANGEDB_CMD_GET_DEPOSIT:
{
unsigned int source_index;
- int ret;
+ enum GNUNET_DB_QueryStatus ret;
struct PERF_TALER_EXCHANGEDB_Data *data;
source_index =
state->cmd[state->i].details.get_deposit.index_deposit;
@@ -1355,7 +1358,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state
*state)
ret = state->plugin->have_deposit (state->plugin->cls,
state->session,
data->data.deposit);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ GNUNET_assert (0 >= ret);
}
break;
@@ -1419,12 +1422,15 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
unsigned int reserve_index;
struct TALER_EXCHANGEDB_ReserveHistory *history;
struct PERF_TALER_EXCHANGEDB_Data *data;
+ enum GNUNET_DB_QueryStatus qs;
reserve_index =
state->cmd[state->i].details.get_reserve_history.index_reserve;
data = &state->cmd[reserve_index].exposed;
- history = state->plugin->get_reserve_history (state->plugin->cls,
- state->session,
-
&data->data.reserve->reserve.pub);
+ qs = state->plugin->get_reserve_history (state->plugin->cls,
+ state->session,
+
&data->data.reserve->reserve.pub,
+ &history);
+ GNUNET_assert (0 >= qs);
GNUNET_assert (NULL != history);
state->plugin->free_reserve_history (state->plugin->cls,
history);
@@ -1443,7 +1449,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state
*state)
case PERF_TALER_EXCHANGEDB_CMD_INSERT_DENOMINATION:
{
unsigned int denom_index;
- int ret;
+ enum GNUNET_DB_QueryStatus ret;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki ;
denom_index =
state->cmd[state->i].details.insert_denomination.index_denom;
@@ -1452,23 +1458,23 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
state->session,
&dki->denom_pub,
&dki->issue);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == ret);
}
break;
case PERF_TALER_EXCHANGEDB_CMD_GET_DENOMINATION:
{
unsigned int denom_index;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
struct PERF_TALER_EXCHANGEDB_Data *data;
denom_index =
state->cmd[state->i].details.get_denomination.index_denom;
data = &state->cmd[denom_index].exposed;
- ret = state->plugin->get_denomination_info (state->plugin->cls,
- state->session,
-
&data->data.dki->denom_pub,
- &data->data.dki->issue);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ qs = state->plugin->get_denomination_info (state->plugin->cls,
+ state->session,
+ &data->data.dki->denom_pub,
+ &data->data.dki->issue);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
}
break;
@@ -1490,31 +1496,31 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_INSERT_WITHDRAW:
{
unsigned int coin_index;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
struct PERF_TALER_EXCHANGEDB_Coin *coin;
coin_index = state->cmd[state->i].details.insert_withdraw.index_coin;
coin = state->cmd[coin_index].exposed.data.coin;
- ret = state->plugin->insert_withdraw_info (state->plugin->cls,
+ qs = state->plugin->insert_withdraw_info (state->plugin->cls,
state->session,
&coin->blind);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
}
break;
case PERF_TALER_EXCHANGEDB_CMD_GET_WITHDRAW:
{
unsigned int source_index;
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
struct PERF_TALER_EXCHANGEDB_Data *data;
source_index =
state->cmd[state->i].details.get_denomination.index_denom;
data = &state->cmd[source_index].exposed;
- ret = state->plugin->get_withdraw_info (state->plugin->cls,
- state->session,
-
&data->data.coin->blind.h_coin_envelope,
- &data->data.coin->blind);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ qs = state->plugin->get_withdraw_info (state->plugin->cls,
+ state->session,
+
&data->data.coin->blind.h_coin_envelope,
+ &data->data.coin->blind);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
}
break;
@@ -1523,13 +1529,16 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
unsigned int coin_index;
struct PERF_TALER_EXCHANGEDB_Coin *coin;
struct TALER_EXCHANGEDB_TransactionList *transactions;
+ enum GNUNET_DB_QueryStatus qs;
coin_index =
state->cmd[state->i].details.get_coin_transaction.index_coin;
coin = state->cmd[coin_index].exposed.data.coin;
- transactions = state->plugin->get_coin_transactions
(state->plugin->cls,
- state->session,
-
&coin->public_info.coin_pub);
- GNUNET_assert (transactions != NULL);
+ qs = state->plugin->get_coin_transactions (state->plugin->cls,
+ state->session,
+
&coin->public_info.coin_pub,
+ &transactions);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+ GNUNET_assert (transactions != NULL);
state->plugin->free_coin_transaction_list (state->plugin->cls,
transactions);
}
@@ -1544,10 +1553,11 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
refresh_session = PERF_TALER_EXCHANGEDB_refresh_session_init ();
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
hash);
- state->plugin->create_refresh_session (state->session,
- state->session,
- hash,
- refresh_session);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+ state->plugin->create_refresh_session (state->session,
+ state->session,
+ hash,
+
refresh_session));
state->cmd[state->i].exposed.data.session_hash = hash;
PERF_TALER_EXCHANGEDB_refresh_session_free (refresh_session);
GNUNET_free (refresh_session);
@@ -1580,11 +1590,12 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
denom_index =
state->cmd[state->i].details.insert_refresh_order.index_denom;
session_hash = state->cmd[hash_index].exposed.data.session_hash;
denom = state->cmd[denom_index].exposed.data.dki;
- state->plugin->insert_refresh_order (state->plugin->cls,
- state->session,
- session_hash,
- 1,
- &denom->denom_pub);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+ state->plugin->insert_refresh_order
(state->plugin->cls,
+ state->session,
+ session_hash,
+ 1,
+
&denom->denom_pub));
}
break;
@@ -1607,18 +1618,18 @@ interpret (struct
PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN:
{
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
unsigned int hash_index;
struct TALER_EXCHANGEDB_RefreshCommitCoin *refresh_commit;
hash_index =
state->cmd[state->i].details.insert_refresh_commit_coin.index_hash;
refresh_commit = PERF_TALER_EXCHANGEDB_refresh_commit_coin_init ();
- ret = state->plugin->insert_refresh_commit_coins (state->plugin->cls,
- state->session,
-
state->cmd[hash_index].exposed.data.session_hash,
- 1,
- refresh_commit);
- GNUNET_assert (GNUNET_OK == ret);
+ qs = state->plugin->insert_refresh_commit_coins (state->plugin->cls,
+ state->session,
+
state->cmd[hash_index].exposed.data.session_hash,
+ 1,
+ refresh_commit);
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
}
break;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c
b/src/exchangedb/plugin_exchangedb_postgres.c
index e251722..8b3fe7f 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -30,38 +30,6 @@
#include "plugin_exchangedb_common.c"
-/**
- * Error code returned by Postgres for deadlock.
- */
-#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
-
-/**
- * Error code returned by Postgres for uniqueness violation.
- */
-#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
-
-/**
- * Error code returned by Postgres on serialization failure.
- */
-#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
-
-
-/**
- * Log a query error.
- *
- * @param result PQ result object of the query that failed
- * @param conn SQL connection that was used
- */
-#define QUERY_ERR(result,conn) \
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
- "Query failed at %s:%u: %s/%s/%s/%s/%s\n", \
- __FILE__, __LINE__, \
- PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \
- PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \
- PQresultErrorMessage (result), \
- PQresStatus (PQresultStatus (result)), \
- PQerrorMessage (conn));
-
/**
* Log a really unexpected PQ error with all the details we can get hold of.
@@ -82,53 +50,6 @@
/**
- * Shorthand for exit jumps. Logs the current line number
- * and jumps to the "EXITIF_exit" label.
- *
- * @param cond condition that must be TRUE to exit with an error
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Execute an SQL statement and log errors on failure. Must be
- * run in a function that has an "SQLEXEC_fail" label to jump
- * to in case the SQL statement failed.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_(conn, sql) \
- do { \
- PGresult *result = PQexec (conn, sql); \
- if (PGRES_COMMAND_OK != PQresultStatus (result)) \
- { \
- BREAK_DB_ERR (result, conn); \
- PQclear (result); \
- goto SQLEXEC_fail; \
- } \
- PQclear (result); \
- } while (0)
-
-
-/**
- * Run an SQL statement, ignoring errors and clearing the result.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_IGNORE_ERROR_(conn, sql) \
- do { \
- PGresult *result = PQexec (conn, sql); \
- PQclear (result); \
- } while (0)
-
-
-
-/**
* Handle for a database session (per-thread, for transactions).
*/
struct TALER_EXCHANGEDB_Session
@@ -138,24 +59,6 @@ struct TALER_EXCHANGEDB_Session
*/
PGconn *conn;
- /**
- * Transaction state. Set to #GNUNET_OK by #postgres_start().
- * Set to #GNUNET_NO if any part of the transaction failed in a
- * transient way (i.e. #PG_DIAG_SQLSTATE_DEADLOCK or
- * #PG_DIAG_SQLSTATE_SERIALIZATION_FAILURE). Set to
- * #GNUNET_SYSERR if any part of the transaction failed in a
- * hard way or if we are not within a transaction scope.
- *
- * If #GNUNET_NO, #postgres_commit() will always just do a
- * rollback and return #GNUNET_NO as well (to retry).
- *
- * If #GNUNET_SYSERR, #postgres_commit() will always just do a
- * rollback and return #GNUNET_SYSERR as well.
- *
- * If #GNUNET_OK, #postgres_commit() will try to commit and
- * return the result from the commit operation.
- */
- int state;
};
@@ -242,318 +145,297 @@ static int
postgres_create_tables (void *cls)
{
struct PostgresClosure *pc = cls;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ /* Denomination table for holding the publicly available information of
+ denominations keys. The denominations are to be referred to using
+ foreign keys. */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS denominations"
+ "(denom_pub_hash BYTEA PRIMARY KEY CHECK
(LENGTH(denom_pub_hash)=64)"
+ ",denom_pub BYTEA NOT NULL"
+ ",master_pub BYTEA NOT NULL CHECK
(LENGTH(master_pub)=32)"
+ ",master_sig BYTEA NOT NULL CHECK
(LENGTH(master_sig)=64)"
+ ",valid_from INT8 NOT NULL"
+ ",expire_withdraw INT8 NOT NULL"
+ ",expire_deposit INT8 NOT NULL"
+ ",expire_legal INT8 NOT NULL"
+ ",coin_val INT8 NOT NULL" /* value of this denom */
+ ",coin_frac INT4 NOT NULL" /* fractional value of
this denom */
+ ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL" /* assuming same currency for fees */
+ ",fee_withdraw_val INT8 NOT NULL"
+ ",fee_withdraw_frac INT4 NOT NULL"
+ ",fee_withdraw_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_deposit_val INT8 NOT NULL"
+ ",fee_deposit_frac INT4 NOT NULL"
+ ",fee_deposit_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refresh_val INT8 NOT NULL"
+ ",fee_refresh_frac INT4 NOT NULL"
+ ",fee_refresh_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refund_val INT8 NOT NULL"
+ ",fee_refund_frac INT4 NOT NULL"
+ ",fee_refund_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")"),
+ /* denomination_revocations table is for remembering which denomination
keys have been revoked */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS
denomination_revocations"
+ "(denom_revocations_serial_id BIGSERIAL"
+ ",denom_pub_hash BYTEA PRIMARY KEY REFERENCES
denominations (denom_pub_hash) ON DELETE CASCADE"
+ ",master_sig BYTEA NOT NULL CHECK
(LENGTH(master_sig)=64)"
+ ");"),
+ /* reserves table is for summarization of a reserve. It is updated when
new
+ funds are added and existing funds are withdrawn. The 'expiration_date'
+ can be used to eventually get rid of reserves that have not been used
+ for a very long time (either by refunding the owner or by greedily
+ grabbing the money, depending on the Exchange's terms of service) */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves"
+ "(reserve_pub BYTEA PRIMARY KEY
CHECK(LENGTH(reserve_pub)=32)"
+ ",account_details TEXT NOT NULL "
+ ",current_balance_val INT8 NOT NULL"
+ ",current_balance_frac INT4 NOT NULL"
+ ",current_balance_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",expiration_date INT8 NOT NULL"
+ ");"),
+ /* index on reserves table */
+ GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_reserve_pub_index ON "
+ "reserves (reserve_pub);"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_expiration_index"
+ " ON reserves (expiration_date);"),
+ /* reserves_in table collects the transactions which transfer funds
+ into the reserve. The rows of this table correspond to each
+ incoming transaction. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_in"
+ "(reserve_in_serial_id BIGSERIAL"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves
(reserve_pub) ON DELETE CASCADE"
+ ",wire_reference BYTEA NOT NULL"
+ ",credit_val INT8 NOT NULL"
+ ",credit_frac INT4 NOT NULL"
+ ",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
+ ",sender_account_details TEXT NOT NULL"
+ ",execution_date INT8 NOT NULL"
+ ",PRIMARY KEY (reserve_pub, wire_reference)"
+ ");"),
+ /* Create indices on reserves_in */
+ GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_in_execution_index"
+ " ON reserves_in (execution_date);"),
+ /* This table contains the data for wire transfers the exchange has
+ executed to close a reserve. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_close "
+ "(close_uuid BIGSERIAL PRIMARY KEY"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves
(reserve_pub) ON DELETE CASCADE"
+ ",execution_date INT8 NOT NULL"
+ ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
+ ",receiver_account TEXT NOT NULL"
+ ",amount_val INT8 NOT NULL"
+ ",amount_frac INT4 NOT NULL"
+ ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
+ ",closing_fee_val INT8 NOT NULL"
+ ",closing_fee_frac INT4 NOT NULL"
+ ",closing_fee_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ");"),
+ GNUNET_PQ_make_try_execute("CREATE INDEX reserves_close_by_reserve "
+ "ON reserves_close(reserve_pub)"),
+ /* Table with the withdraw operations that have been performed on a
reserve.
+ The 'h_blind_ev' is the hash of the blinded coin. It serves as a primary
+ key, as (broken) clients that use a non-random coin and blinding factor
+ should fail to even withdraw, as otherwise the coins will fail to
deposit
+ (as they really must be unique). */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves_out"
+ "(reserve_out_serial_id BIGSERIAL"
+ ",h_blind_ev BYTEA PRIMARY KEY"
+ ",denom_pub_hash BYTEA NOT NULL REFERENCES
denominations (denom_pub_hash) ON DELETE CASCADE"
+ ",denom_sig BYTEA NOT NULL"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves
(reserve_pub) ON DELETE CASCADE"
+ ",reserve_sig BYTEA NOT NULL CHECK
(LENGTH(reserve_sig)=64)"
+ ",execution_date INT8 NOT NULL"
+ ",amount_with_fee_val INT8 NOT NULL"
+ ",amount_with_fee_frac INT4 NOT NULL"
+ ",amount_with_fee_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ");"),
+ /* Index blindcoins(reserve_pub) for get_reserves_out statement */
+ GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_out_reserve_pub_index
ON"
+ " reserves_out (reserve_pub)"),
+ GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_out_execution_date ON "
+ "reserves_out (execution_date)"),
+ /* Table with coins that have been (partially) spent, used to track
+ coin information only once. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS known_coins "
+ "(coin_pub BYTEA NOT NULL PRIMARY KEY CHECK
(LENGTH(coin_pub)=32)"
+ ",denom_pub_hash BYTEA NOT NULL REFERENCES
denominations (denom_pub_hash) ON DELETE CASCADE"
+ ",denom_sig BYTEA NOT NULL"
+ ");"),
+ /**
+ * The DB will show negative values for some values of the following
fields as
+ * we use them as 16 bit unsigned integers
+ * @a num_newcoins
+ * @a noreveal_index
+ * Do not do arithmetic in SQL on these fields.
+ * NOTE: maybe we should instead forbid values >= 2^15 categorically?
+ */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_sessions "
+ "(melt_serial_id BIGSERIAL"
+ ",session_hash BYTEA PRIMARY KEY CHECK
(LENGTH(session_hash)=64)"
+ ",old_coin_pub BYTEA NOT NULL REFERENCES
known_coins (coin_pub) ON DELETE CASCADE"
+ ",old_coin_sig BYTEA NOT NULL
CHECK(LENGTH(old_coin_sig)=64)"
+ ",amount_with_fee_val INT8 NOT NULL"
+ ",amount_with_fee_frac INT4 NOT NULL"
+ ",amount_with_fee_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",num_newcoins INT2 NOT NULL"
+ ",noreveal_index INT2 NOT NULL"
+ ");"),
+ /* Table with information about the desired denominations to be created
+ during a refresh operation; contains the denomination key for each
+ of the coins (for a given refresh session) */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_order "
+ "(session_hash BYTEA NOT NULL REFERENCES
refresh_sessions (session_hash) ON DELETE CASCADE"
+ ",newcoin_index INT2 NOT NULL "
+ ",denom_pub_hash BYTEA NOT NULL REFERENCES
denominations (denom_pub_hash) ON DELETE CASCADE"
+ ",PRIMARY KEY (session_hash, newcoin_index)"
+ ");"),
+ /* Table with the commitments for a refresh operation; includes
+ the session_hash for which this is the link information, the
+ oldcoin index and the cut-and-choose index (from 0 to
#TALER_CNC_KAPPA-1),
+ as well as the actual link data (the transfer public key and the
encrypted
+ link secret) */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS
refresh_transfer_public_key "
+ "(session_hash BYTEA NOT NULL PRIMARY KEY
REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE"
+ ",transfer_pub BYTEA NOT NULL
CHECK(LENGTH(transfer_pub)=32)"
+ ");"),
+ /* Table with the commitments for the new coins that are to be created
+ during a melting session. Includes the session, the cut-and-choose
+ index and the index of the new coin, and the envelope of the new
+ coin to be signed, as well as the encrypted information about the
+ private key and the blinding factor for the coin (for verification
+ in case this newcoin_index is chosen to be revealed) */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_commit_coin "
+ "(session_hash BYTEA NOT NULL REFERENCES
refresh_sessions (session_hash) ON DELETE CASCADE"
+ ",newcoin_index INT2 NOT NULL"
+ ",coin_ev BYTEA NOT NULL"
+ ",UNIQUE (session_hash, newcoin_index)"
+ ");"),
+ GNUNET_PQ_make_try_execute("CREATE INDEX
refresh_commit_coin_session_hash_index "
+ "ON refresh_commit_coin(session_hash,
newcoin_index)"),
+ /* Table with the signatures over coins generated during a refresh
+ operation. Needed to answer /refresh/link queries later. Stores
+ the coin signatures under the respective session hash and index. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_out "
+ "(session_hash BYTEA NOT NULL REFERENCES
refresh_sessions (session_hash) ON DELETE CASCADE"
+ ",newcoin_index INT2 NOT NULL"
+ ",ev_sig BYTEA NOT NULL"
+ ",UNIQUE (session_hash, newcoin_index)"
+ ");"),
+ GNUNET_PQ_make_try_execute("CREATE INDEX refresh_out_session_hash_index "
+ "ON refresh_out(session_hash, newcoin_index)"),
+ /* This table contains the wire transfers the exchange is supposed to
+ execute to transmit funds to the merchants (and manage refunds). */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS deposits "
+ "(deposit_serial_id BIGSERIAL PRIMARY KEY"
+ ",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"
+ ",amount_with_fee_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",timestamp INT8 NOT NULL"
+ ",refund_deadline INT8 NOT NULL"
+ ",wire_deadline INT8 NOT NULL"
+ ",merchant_pub BYTEA NOT NULL CHECK
(LENGTH(merchant_pub)=32)"
+ ",h_contract_terms BYTEA NOT NULL CHECK
(LENGTH(h_contract_terms)=64)"
+ ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
+ ",coin_sig BYTEA NOT NULL CHECK
(LENGTH(coin_sig)=64)"
+ ",wire TEXT NOT NULL"
+ ",tiny BOOLEAN NOT NULL DEFAULT false"
+ ",done BOOLEAN NOT NULL DEFAULT false"
+ ",UNIQUE (coin_pub, h_contract_terms, merchant_pub)"
+ ");"),
+ /* Index for get_deposit statement on coin_pub, h_contract_terms and
merchant_pub */
+ GNUNET_PQ_make_try_execute("CREATE INDEX deposits_coin_pub_index "
+ "ON deposits(coin_pub, h_contract_terms,
merchant_pub)"),
+ /* Table with information about coins that have been refunded. (Technically
+ one of the deposit operations that a coin was involved with is
refunded.)*/
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refunds "
+ "(refund_serial_id BIGSERIAL"
+ ",coin_pub BYTEA NOT NULL REFERENCES known_coins
(coin_pub) ON DELETE CASCADE"
+ ",merchant_pub BYTEA NOT NULL
CHECK(LENGTH(merchant_pub)=32)"
+ ",merchant_sig BYTEA NOT NULL
CHECK(LENGTH(merchant_sig)=64)"
+ ",h_contract_terms BYTEA NOT NULL
CHECK(LENGTH(h_contract_terms)=64)"
+ ",rtransaction_id INT8 NOT NULL"
+ ",amount_with_fee_val INT8 NOT NULL"
+ ",amount_with_fee_frac INT4 NOT NULL"
+ ",amount_with_fee_curr
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",PRIMARY KEY (coin_pub, merchant_pub,
h_contract_terms, rtransaction_id)" /* this combo must be unique, and we
usually select by coin_pub */
+ ");"),
+ /* This table contains the data for
+ wire transfers the exchange has executed. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS wire_out "
+ "(wireout_uuid BIGSERIAL PRIMARY KEY"
+ ",execution_date INT8 NOT NULL"
+ ",wtid_raw BYTEA UNIQUE NOT NULL CHECK
(LENGTH(wtid_raw)=" TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")"
+ ",wire_target TEXT NOT NULL"
+ ",amount_val INT8 NOT NULL"
+ ",amount_frac INT4 NOT NULL"
+ ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
+ ");"),
+ /* Table for the tracking API, mapping from wire transfer identifiers
+ to transactions and back */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS aggregation_tracking "
+ "(aggregation_serial_id BIGSERIAL"
+ ",deposit_serial_id INT8 PRIMARY KEY REFERENCES
deposits (deposit_serial_id) ON DELETE CASCADE"
+ ",wtid_raw BYTEA CONSTRAINT wire_out_ref
REFERENCES wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE"
+ ");"),
+ /* Index for lookup_transactions statement on wtid */
+ GNUNET_PQ_make_try_execute("CREATE INDEX aggregation_tracking_wtid_index "
+ "ON aggregation_tracking(wtid_raw)"),
+ /* Table for the wire fees. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS wire_fee "
+ "(wire_method VARCHAR NOT NULL"
+ ",start_date INT8 NOT NULL"
+ ",end_date INT8 NOT NULL"
+ ",wire_fee_val INT8 NOT NULL"
+ ",wire_fee_frac INT4 NOT NULL"
+ ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR")
NOT NULL"
+ ",master_sig BYTEA NOT NULL CHECK
(LENGTH(master_sig)=64)"
+ ",PRIMARY KEY (wire_method, start_date)" /* this
combo must be unique */
+ ");"),
+ /* Index for lookup_transactions statement on wtid */
+ GNUNET_PQ_make_try_execute("CREATE INDEX aggregation_tracking_wtid_index "
+ "ON aggregation_tracking(wtid_raw);"),
+ /* Table for /payback information */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS payback "
+ "(payback_uuid BIGSERIAL"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves
(reserve_pub) ON DELETE CASCADE"
+ ",coin_pub BYTEA NOT NULL REFERENCES known_coins
(coin_pub) ON DELETE CASCADE"
+ ",coin_sig BYTEA NOT NULL
CHECK(LENGTH(coin_sig)=64)"
+ ",coin_blind BYTEA NOT NULL
CHECK(LENGTH(coin_blind)=32)"
+ ",amount_val INT8 NOT NULL"
+ ",amount_frac INT4 NOT NULL"
+ ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT
NULL"
+ ",timestamp INT8 NOT NULL"
+ ",h_blind_ev BYTEA NOT NULL REFERENCES reserves_out
(h_blind_ev) ON DELETE CASCADE"
+ ");"),
+ GNUNET_PQ_make_try_execute("CREATE INDEX payback_by_coin_index "
+ "ON payback(coin_pub);"),
+ GNUNET_PQ_make_try_execute("CREATE INDEX payback_by_reserve_index "
+ "ON payback(reserve_pub);"),
+
+ /* This table contains the pre-commit data for
+ wire transfers the exchange is about to execute. */
+ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS prewire "
+ "(prewire_uuid BIGSERIAL PRIMARY KEY"
+ ",type TEXT NOT NULL"
+ ",finished BOOLEAN NOT NULL DEFAULT false"
+ ",buf BYTEA NOT NULL"
+ ");"),
+ /* Index for prepare_data_iterate statement */
+ GNUNET_PQ_make_try_execute("CREATE INDEX prepare_iteration_index "
+ "ON prewire(type,finished);"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
PGconn *conn;
+ int ret;
conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == conn)
return GNUNET_SYSERR;
-#define SQLEXEC(sql) SQLEXEC_(conn, sql);
-#define SQLEXEC_INDEX(sql) SQLEXEC_IGNORE_ERROR_(conn, sql);
- /* Denomination table for holding the publicly available information of
- denominations keys. The denominations are to be referred to using
- foreign keys. */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS denominations"
- "(denom_pub_hash BYTEA PRIMARY KEY CHECK
(LENGTH(denom_pub_hash)=64)"
- ",denom_pub BYTEA NOT NULL"
- ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
- ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
- ",valid_from INT8 NOT NULL"
- ",expire_withdraw INT8 NOT NULL"
- ",expire_deposit INT8 NOT NULL"
- ",expire_legal INT8 NOT NULL"
- ",coin_val INT8 NOT NULL" /* value of this denom */
- ",coin_frac INT4 NOT NULL" /* fractional value of this denom */
- ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" /* assuming
same currency for fees */
- ",fee_withdraw_val INT8 NOT NULL"
- ",fee_withdraw_frac INT4 NOT NULL"
- ",fee_withdraw_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",fee_deposit_val INT8 NOT NULL"
- ",fee_deposit_frac INT4 NOT NULL"
- ",fee_deposit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",fee_refresh_val INT8 NOT NULL"
- ",fee_refresh_frac INT4 NOT NULL"
- ",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",fee_refund_val INT8 NOT NULL"
- ",fee_refund_frac INT4 NOT NULL"
- ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
- /* denomination_revocations table is for remembering which denomination keys
have been revoked */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS denomination_revocations"
- "(denom_revocations_serial_id BIGSERIAL"
- ",denom_pub_hash BYTEA PRIMARY KEY REFERENCES denominations
(denom_pub_hash) ON DELETE CASCADE"
- ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
- ")");
-
- /* reserves table is for summarization of a reserve. It is updated when new
- funds are added and existing funds are withdrawn. The 'expiration_date'
- can be used to eventually get rid of reserves that have not been used
- for a very long time (either by refunding the owner or by greedily
- grabbing the money, depending on the Exchange's terms of service) */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves"
- "(reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)"
- ",account_details TEXT NOT NULL "
- ",current_balance_val INT8 NOT NULL"
- ",current_balance_frac INT4 NOT NULL"
- ",current_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",expiration_date INT8 NOT NULL"
- ")");
- /* index on reserves table */
- SQLEXEC_INDEX ("CREATE INDEX reserves_reserve_pub_index ON "
- "reserves (reserve_pub)");
- SQLEXEC_INDEX ("CREATE INDEX reserves_expiration_index"
- " ON reserves (expiration_date);");
-
- /* reserves_in table collects the transactions which transfer funds
- into the reserve. The rows of this table correspond to each
- incoming transaction. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in"
- "(reserve_in_serial_id BIGSERIAL"
- ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON
DELETE CASCADE"
- ",wire_reference BYTEA NOT NULL"
- ",credit_val INT8 NOT NULL"
- ",credit_frac INT4 NOT NULL"
- ",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",sender_account_details TEXT NOT NULL"
- ",execution_date INT8 NOT NULL"
- ",PRIMARY KEY (reserve_pub, wire_reference)"
- ");");
- /* Create indices on reserves_in */
- SQLEXEC_INDEX ("CREATE INDEX reserves_in_execution_index"
- " ON reserves_in (execution_date);");
-
- /* This table contains the data for wire transfers the exchange has
- executed to close a reserve. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_close "
- "(close_uuid BIGSERIAL PRIMARY KEY"
- ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON
DELETE CASCADE"
- ",execution_date INT8 NOT NULL"
- ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
- ",receiver_account TEXT NOT NULL"
- ",amount_val INT8 NOT NULL"
- ",amount_frac INT4 NOT NULL"
- ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",closing_fee_val INT8 NOT NULL"
- ",closing_fee_frac INT4 NOT NULL"
- ",closing_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
- SQLEXEC_INDEX("CREATE INDEX reserves_close_by_reserve "
- "ON reserves_close(reserve_pub)");
-
- /* Table with the withdraw operations that have been performed on a reserve.
- The 'h_blind_ev' is the hash of the blinded coin. It serves as a primary
- key, as (broken) clients that use a non-random coin and blinding factor
- should fail to even withdraw, as otherwise the coins will fail to deposit
- (as they really must be unique). */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves_out"
- "(reserve_out_serial_id BIGSERIAL"
- ",h_blind_ev BYTEA PRIMARY KEY"
- ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations
(denom_pub_hash) ON DELETE CASCADE"
- ",denom_sig BYTEA NOT NULL"
- ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON
DELETE CASCADE"
- ",reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)"
- ",execution_date INT8 NOT NULL"
- ",amount_with_fee_val INT8 NOT NULL"
- ",amount_with_fee_frac INT4 NOT NULL"
- ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ");");
- /* Index blindcoins(reserve_pub) for get_reserves_out statement */
- SQLEXEC_INDEX ("CREATE INDEX reserves_out_reserve_pub_index ON"
- " reserves_out (reserve_pub)");
- SQLEXEC_INDEX ("CREATE INDEX reserves_out_execution_date ON "
- "reserves_out (execution_date)");
- /* Table with coins that have been (partially) spent, used to track
- coin information only once. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins "
- "(coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32)"
- ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations
(denom_pub_hash) ON DELETE CASCADE"
- ",denom_sig BYTEA NOT NULL"
- ")");
- /**
- * The DB will show negative values for some values of the following fields
as
- * we use them as 16 bit unsigned integers
- * @a num_newcoins
- * @a noreveal_index
- * Do not do arithmetic in SQL on these fields.
- * NOTE: maybe we should instead forbid values >= 2^15 categorically?
- */
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
- "(melt_serial_id BIGSERIAL"
- ",session_hash BYTEA PRIMARY KEY CHECK (LENGTH(session_hash)=64)"
- ",old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON
DELETE CASCADE"
- ",old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)"
- ",amount_with_fee_val INT8 NOT NULL"
- ",amount_with_fee_frac INT4 NOT NULL"
- ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",num_newcoins INT2 NOT NULL"
- ",noreveal_index INT2 NOT NULL"
- ")");
-
- /* Table with information about the desired denominations to be created
- during a refresh operation; contains the denomination key for each
- of the coins (for a given refresh session) */
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order "
- "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions
(session_hash) ON DELETE CASCADE"
- ",newcoin_index INT2 NOT NULL "
- ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations
(denom_pub_hash) ON DELETE CASCADE"
- ",PRIMARY KEY (session_hash, newcoin_index)"
- ")");
-
- /* Table with the commitments for a refresh operation; includes
- the session_hash for which this is the link information, the
- oldcoin index and the cut-and-choose index (from 0 to #TALER_CNC_KAPPA-1),
- as well as the actual link data (the transfer public key and the encrypted
- link secret) */
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_transfer_public_key "
- "(session_hash BYTEA NOT NULL PRIMARY KEY REFERENCES
refresh_sessions (session_hash) ON DELETE CASCADE"
- ",transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)"
- ")");
-
- /* Table with the commitments for the new coins that are to be created
- during a melting session. Includes the session, the cut-and-choose
- index and the index of the new coin, and the envelope of the new
- coin to be signed, as well as the encrypted information about the
- private key and the blinding factor for the coin (for verification
- in case this newcoin_index is chosen to be revealed) */
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin "
- "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions
(session_hash) ON DELETE CASCADE"
- ",newcoin_index INT2 NOT NULL"
- ",coin_ev BYTEA NOT NULL"
- ",UNIQUE (session_hash, newcoin_index)"
- ")");
- SQLEXEC_INDEX("CREATE INDEX refresh_commit_coin_session_hash_index "
- "ON refresh_commit_coin(session_hash, newcoin_index)");
-
-
- /* Table with the signatures over coins generated during a refresh
- operation. Needed to answer /refresh/link queries later. Stores
- the coin signatures under the respective session hash and index. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_out "
- "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions
(session_hash) ON DELETE CASCADE"
- ",newcoin_index INT2 NOT NULL"
- ",ev_sig BYTEA NOT NULL"
- ",UNIQUE (session_hash, newcoin_index)"
- ")");
- SQLEXEC_INDEX("CREATE INDEX refresh_out_session_hash_index "
- "ON refresh_out(session_hash, newcoin_index)");
-
- /* This table contains the wire transfers the exchange is supposed to
- execute to transmit funds to the merchants (and manage refunds). */
- SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
- "(deposit_serial_id BIGSERIAL PRIMARY KEY"
- ",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"
- ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",timestamp INT8 NOT NULL"
- ",refund_deadline INT8 NOT NULL"
- ",wire_deadline INT8 NOT NULL"
- ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
- ",h_contract_terms BYTEA NOT NULL CHECK
(LENGTH(h_contract_terms)=64)"
- ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
- ",coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)"
- ",wire TEXT NOT NULL"
- ",tiny BOOLEAN NOT NULL DEFAULT false"
- ",done BOOLEAN NOT NULL DEFAULT false"
- ",UNIQUE (coin_pub, h_contract_terms, merchant_pub)"
- ")");
- /* Index for get_deposit statement on coin_pub, h_contract_terms and
merchant_pub */
- SQLEXEC_INDEX("CREATE INDEX deposits_coin_pub_index "
- "ON deposits(coin_pub, h_contract_terms, merchant_pub)");
-
- /* Table with information about coins that have been refunded. (Technically
- one of the deposit operations that a coin was involved with is
refunded.)*/
- SQLEXEC("CREATE TABLE IF NOT EXISTS refunds "
- "(refund_serial_id BIGSERIAL"
- ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE
CASCADE"
- ",merchant_pub BYTEA NOT NULL CHECK(LENGTH(merchant_pub)=32)"
- ",merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)"
- ",h_contract_terms BYTEA NOT NULL CHECK(LENGTH(h_contract_terms)=64)"
- ",rtransaction_id INT8 NOT NULL"
- ",amount_with_fee_val INT8 NOT NULL"
- ",amount_with_fee_frac INT4 NOT NULL"
- ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",PRIMARY KEY (coin_pub, merchant_pub, h_contract_terms,
rtransaction_id)" /* this combo must be unique, and we usually select by
coin_pub */
- ") ");
-
- /* This table contains the data for
- wire transfers the exchange has executed. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS wire_out "
- "(wireout_uuid BIGSERIAL PRIMARY KEY"
- ",execution_date INT8 NOT NULL"
- ",wtid_raw BYTEA UNIQUE NOT NULL CHECK (LENGTH(wtid_raw)="
TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")"
- ",wire_target TEXT NOT NULL"
- ",amount_val INT8 NOT NULL"
- ",amount_frac INT4 NOT NULL"
- ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ")");
-
- /* Table for the tracking API, mapping from wire transfer identifiers
- to transactions and back */
- SQLEXEC("CREATE TABLE IF NOT EXISTS aggregation_tracking "
- "(aggregation_serial_id BIGSERIAL"
- ",deposit_serial_id INT8 PRIMARY KEY REFERENCES deposits
(deposit_serial_id) ON DELETE CASCADE"
- ",wtid_raw BYTEA CONSTRAINT wire_out_ref REFERENCES
wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE"
- ")");
- /* Index for lookup_transactions statement on wtid */
- SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
- "ON aggregation_tracking(wtid_raw)");
-
-
- /* Table for the wire fees. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS wire_fee "
- "(wire_method VARCHAR NOT NULL"
- ",start_date INT8 NOT NULL"
- ",end_date INT8 NOT NULL"
- ",wire_fee_val INT8 NOT NULL"
- ",wire_fee_frac INT4 NOT NULL"
- ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
- ",PRIMARY KEY (wire_method, start_date)" /* this combo must be
unique */
- ")");
- /* Index for lookup_transactions statement on wtid */
- SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
- "ON aggregation_tracking(wtid_raw)");
-
- /* Table for /payback information */
- SQLEXEC("CREATE TABLE IF NOT EXISTS payback "
- "(payback_uuid BIGSERIAL"
- ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON
DELETE CASCADE"
- ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON
DELETE CASCADE"
- ",coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)"
- ",coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)"
- ",amount_val INT8 NOT NULL"
- ",amount_frac INT4 NOT NULL"
- ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
- ",timestamp INT8 NOT NULL"
- ",h_blind_ev BYTEA NOT NULL REFERENCES reserves_out (h_blind_ev) ON
DELETE CASCADE"
- ")");
- SQLEXEC_INDEX("CREATE INDEX payback_by_coin_index "
- "ON payback(coin_pub)");
- SQLEXEC_INDEX("CREATE INDEX payback_by_reserve_index "
- "ON payback(reserve_pub)");
-
- /* This table contains the pre-commit data for
- wire transfers the exchange is about to execute. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS prewire "
- "(prewire_uuid BIGSERIAL PRIMARY KEY"
- ",type TEXT NOT NULL"
- ",finished BOOLEAN NOT NULL DEFAULT false"
- ",buf BYTEA NOT NULL"
- ")");
- /* Index for prepare_data_iterate statement */
- SQLEXEC_INDEX("CREATE INDEX prepare_iteration_index "
- "ON prewire(type,finished)");
-
-
-#undef SQLEXEC
-#undef SQLEXEC_INDEX
-
- PQfinish (conn);
- return GNUNET_OK;
-
- SQLEXEC_fail:
+ ret = GNUNET_PQ_exec_statements (conn,
+ es);
PQfinish (conn);
- return GNUNET_SYSERR;
+ return ret;
}
@@ -566,1050 +448,1015 @@ postgres_create_tables (void *cls)
static int
postgres_prepare (PGconn *db_conn)
{
- PGresult *result;
-
-#define PREPARE(name, sql, ...) \
- do { \
- result = PQprepare (db_conn, name, sql, __VA_ARGS__); \
- if (PGRES_COMMAND_OK != PQresultStatus (result)) \
- { \
- BREAK_DB_ERR (result, db_conn); \
- PQclear (result); result = NULL; \
- return GNUNET_SYSERR; \
- } \
- PQclear (result); result = NULL; \
- } while (0);
-
- /* Used in #postgres_insert_denomination_info() */
- PREPARE ("denomination_insert",
- "INSERT INTO denominations "
- "(denom_pub_hash"
- ",denom_pub"
- ",master_pub"
- ",master_sig"
- ",valid_from"
- ",expire_withdraw"
- ",expire_deposit"
- ",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",coin_curr" /* assuming same currency for fees */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_withdraw_curr" /* must match coin_curr */
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_deposit_curr" /* must match coin_curr */
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refresh_curr" /* must match coin_curr */
- ",fee_refund_val"
- ",fee_refund_frac"
- ",fee_refund_curr" /* must match coin_curr */
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12, $13, $14, $15, $16, $17, $18,"
- " $19, $20, $21, $22, $23);",
- 23, NULL);
-
- /* Used in #postgres_get_denomination_info() */
- PREPARE ("denomination_get",
- "SELECT"
- " master_pub"
- ",master_sig"
- ",valid_from"
- ",expire_withdraw"
- ",expire_deposit"
- ",expire_legal"
- ",coin_val" /* value of this denom */
- ",coin_frac" /* fractional value of this denom */
- ",coin_curr" /* assuming same currency for fees */
- ",fee_withdraw_val"
- ",fee_withdraw_frac"
- ",fee_withdraw_curr" /* must match coin_curr */
- ",fee_deposit_val"
- ",fee_deposit_frac"
- ",fee_deposit_curr" /* must match coin_curr */
- ",fee_refresh_val"
- ",fee_refresh_frac"
- ",fee_refresh_curr" /* must match coin_curr */
- ",fee_refund_val"
- ",fee_refund_frac"
- ",fee_refund_curr" /* must match coin_curr */
- " FROM denominations"
- " WHERE denom_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_insert_denomination_revocation() */
- PREPARE ("denomination_revocation_insert",
- "INSERT INTO denomination_revocations "
- "(denom_pub_hash"
- ",master_sig"
- ") VALUES "
- "($1, $2);",
- 2, NULL);
-
- /* Used in #postgres_get_denomination_revocation() */
- PREPARE ("denomination_revocation_get",
- "SELECT"
- " master_sig"
- ",denom_revocations_serial_id"
- " FROM denomination_revocations"
- " WHERE denom_pub_hash=$1;",
- 1, NULL);
-
-
- /* Used in #postgres_reserve_get() */
- PREPARE ("reserve_get",
- "SELECT"
- " current_balance_val"
- ",current_balance_frac"
- ",current_balance_curr"
- ",expiration_date"
- " FROM reserves"
- " WHERE reserve_pub=$1"
- " LIMIT 1;",
- 1, NULL);
-
- /* Used in #postgres_reserves_in_insert() when the reserve is new */
- PREPARE ("reserve_create",
- "INSERT INTO reserves "
- "(reserve_pub"
- ",account_details"
- ",current_balance_val"
- ",current_balance_frac"
- ",current_balance_curr"
- ",expiration_date"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6);",
- 6, NULL);
-
- /* Used in #postgres_insert_reserve_closed() */
- PREPARE ("reserves_close_insert",
- "INSERT INTO reserves_close "
- "(reserve_pub"
- ",execution_date"
- ",wtid"
- ",receiver_account"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",closing_fee_curr"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
- 10, NULL);
-
- /* Used in #postgres_reserves_update() when the reserve is updated */
- PREPARE ("reserve_update",
- "UPDATE reserves"
- " SET"
- " expiration_date=$1 "
- ",current_balance_val=$2 "
- ",current_balance_frac=$3 "
- "WHERE current_balance_curr=$4 AND reserve_pub=$5",
- 5, NULL);
-
- /* Used in #postgres_reserves_in_insert() to store transaction details */
- PREPARE ("reserves_in_add_transaction",
- "INSERT INTO reserves_in "
- "(reserve_pub"
- ",wire_reference"
- ",credit_val"
- ",credit_frac"
- ",credit_curr"
- ",sender_account_details"
- ",execution_date"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7);",
- 7, NULL);
-
-
- /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
- transactions for reserves with serial id '\geq' the given parameter */
- PREPARE ("reserves_in_get_latest_wire_reference",
- "SELECT"
- " wire_reference"
- " FROM reserves_in"
- " ORDER BY reserve_in_serial_id DESC LIMIT 1",
- 0, NULL);
-
- /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
- transactions for reserves with serial id '\geq' the given parameter */
- PREPARE ("audit_reserves_in_get_transactions_incr",
- "SELECT"
- " reserve_pub"
- ",wire_reference"
- ",credit_val"
- ",credit_frac"
- ",credit_curr"
- ",execution_date"
- ",sender_account_details"
- ",reserve_in_serial_id"
- " FROM reserves_in"
- " WHERE reserve_in_serial_id>=$1"
- " ORDER BY reserve_in_serial_id",
- 1, NULL);
-
- /* Used in #postgres_get_reserve_history() to obtain inbound transactions
- for a reserve */
- PREPARE ("reserves_in_get_transactions",
- "SELECT"
- " wire_reference"
- ",credit_val"
- ",credit_frac"
- ",credit_curr"
- ",execution_date"
- ",sender_account_details"
- " FROM reserves_in"
- " WHERE reserve_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_insert_withdraw_info() to store
- the signature of a blinded coin with the blinded coin's
- details before returning it during /reserve/withdraw. We store
- the coin's denomination information (public key, signature)
- and the blinded message as well as the reserve that the coin
- is being withdrawn from and the signature of the message
- authorizing the withdrawal. */
- PREPARE ("insert_withdraw_info",
- "INSERT INTO reserves_out "
- "(h_blind_ev"
- ",denom_pub_hash"
- ",denom_sig"
- ",reserve_pub"
- ",reserve_sig"
- ",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
- 9, NULL);
-
- /* Used in #postgres_get_withdraw_info() to
- locate the response for a /reserve/withdraw request
- using the hash of the blinded message. Used to
- make sure /reserve/withdraw requests are idempotent. */
- PREPARE ("get_withdraw_info",
- "SELECT"
- " denom.denom_pub"
- ",denom_sig"
- ",reserve_sig"
- ",reserve_pub"
- ",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_withdraw_val"
- ",denom.fee_withdraw_frac"
- ",denom.fee_withdraw_curr"
- " FROM reserves_out"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE h_blind_ev=$1",
- 1, NULL);
-
- /* Used during #postgres_get_reserve_history() to
- obtain all of the /reserve/withdraw operations that
- have been performed on a given reserve. (i.e. to
- demonstrate double-spending) */
- PREPARE ("get_reserves_out",
- "SELECT"
- " h_blind_ev"
- ",denom.denom_pub"
- ",denom_sig"
- ",reserve_sig"
- ",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_withdraw_val"
- ",denom.fee_withdraw_frac"
- ",denom.fee_withdraw_curr"
- " FROM reserves_out"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE reserve_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_select_reserves_out_above_serial_id() */
- PREPARE ("audit_get_reserves_out_incr",
- "SELECT"
- " h_blind_ev"
- ",denom.denom_pub"
- ",denom_sig"
- ",reserve_sig"
- ",reserve_pub"
- ",execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",reserve_out_serial_id"
- " FROM reserves_out"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE reserve_out_serial_id>=$1"
- " ORDER BY reserve_out_serial_id ASC",
- 1, NULL);
-
- /* Used in #postgres_get_refresh_session() to fetch
- high-level information about a refresh session */
- PREPARE ("get_refresh_session",
- "SELECT"
- " old_coin_pub"
- ",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_refresh_val "
- ",denom.fee_refresh_frac "
- ",denom.fee_refresh_curr "
- ",num_newcoins"
- ",noreveal_index"
- " FROM refresh_sessions"
- " JOIN known_coins ON (refresh_sessions.old_coin_pub =
known_coins.coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE session_hash=$1 ",
- 1, NULL);
-
- /* Used in #postgres_select_refreshs_above_serial_id() to fetch
- refresh session with id '\geq' the given parameter */
- PREPARE ("audit_get_refresh_sessions_incr",
- "SELECT"
- " denom.denom_pub"
- ",old_coin_pub"
- ",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",num_newcoins"
- ",noreveal_index"
- ",melt_serial_id"
- ",session_hash"
- " FROM refresh_sessions"
- " JOIN known_coins kc ON (refresh_sessions.old_coin_pub =
kc.coin_pub)"
- " JOIN denominations denom ON (kc.denom_pub_hash =
denom.denom_pub_hash)"
- " WHERE melt_serial_id>=$1"
- " ORDER BY melt_serial_id ASC",
- 1, NULL);
-
- /* Used in #postgres_create_refresh_session() to store
- high-level information about a refresh session */
- PREPARE ("insert_refresh_session",
- "INSERT INTO refresh_sessions "
- "(session_hash "
- ",old_coin_pub "
- ",old_coin_sig "
- ",amount_with_fee_val "
- ",amount_with_fee_frac "
- ",amount_with_fee_curr "
- ",num_newcoins "
- ",noreveal_index "
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);",
- 8, NULL);
-
- /* Used in #postgres_get_known_coin() to fetch
- the denomination public key and signature for
- a coin known to the exchange. */
- PREPARE ("get_known_coin",
- "SELECT"
- " denom.denom_pub"
- ",denom_sig"
- " FROM known_coins"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE coin_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_insert_known_coin() to store
- the denomination public key and signature for
- a coin known to the exchange. */
- PREPARE ("insert_known_coin",
- "INSERT INTO known_coins "
- "(coin_pub"
- ",denom_pub_hash"
- ",denom_sig"
- ") VALUES "
- "($1,$2,$3);",
- 3, NULL);
-
- /* Store information about the desired denominations for a
- refresh operation, used in #postgres_insert_refresh_order() */
- PREPARE ("insert_refresh_order",
- "INSERT INTO refresh_order "
- "(newcoin_index "
- ",session_hash "
- ",denom_pub_hash "
- ") VALUES "
- "($1, $2, $3);",
- 3, NULL);
-
- /* Obtain information about the desired denominations for a
- refresh operation, used in #postgres_get_refresh_order() */
- PREPARE ("get_refresh_order",
- "SELECT denom_pub"
- " FROM refresh_order"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE session_hash=$1 AND newcoin_index=$2",
- 2, NULL);
-
- /* Query the 'refresh_sessions' by coin public key */
- PREPARE ("get_refresh_session_by_coin",
- "SELECT"
- " session_hash"
- ",old_coin_sig"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_refresh_val "
- ",denom.fee_refresh_frac "
- ",denom.fee_refresh_curr "
- " FROM refresh_sessions"
- " JOIN known_coins ON (refresh_sessions.old_coin_pub =
known_coins.coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE old_coin_pub=$1",
- 1, NULL);
-
- /* Fetch refunds with rowid '\geq' the given parameter */
- PREPARE ("audit_get_refunds_incr",
- "SELECT"
- " merchant_pub"
- ",merchant_sig"
- ",h_contract_terms"
- ",rtransaction_id"
- ",denom.denom_pub"
- ",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",refund_serial_id"
- " FROM refunds"
- " JOIN known_coins kc USING (coin_pub)"
- " JOIN denominations denom ON (kc.denom_pub_hash =
denom.denom_pub_hash)"
- " WHERE refund_serial_id>=$1"
- " ORDER BY refund_serial_id ASC",
- 1, NULL);
-
- /* Query the 'refunds' by coin public key */
- PREPARE ("get_refunds_by_coin",
- "SELECT"
- " merchant_pub"
- ",merchant_sig"
- ",h_contract_terms"
- ",rtransaction_id"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_refund_val "
- ",denom.fee_refund_frac "
- ",denom.fee_refund_curr "
- " FROM refunds"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE coin_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_insert_transfer_public_key() to
- store commitments */
- PREPARE ("insert_transfer_public_key",
- "INSERT INTO refresh_transfer_public_key "
- "(session_hash"
- ",transfer_pub"
- ") VALUES "
- "($1, $2);",
- 3, NULL);
-
- /* Used in #postgres_get_refresh_transfer_public_key() to
- retrieve original commitments during /refresh/reveal */
- PREPARE ("get_refresh_transfer_public_key",
- "SELECT"
- " transfer_pub"
- " FROM refresh_transfer_public_key"
- " WHERE session_hash=$1",
- 1, NULL);
-
- /* Used in #postgres_insert_refresh_commit_coins() to
- store coin commitments. */
- PREPARE ("insert_refresh_commit_coin",
- "INSERT INTO refresh_commit_coin "
- "(session_hash"
- ",newcoin_index"
- ",coin_ev"
- ") VALUES "
- "($1, $2, $3);",
- 3, NULL);
-
- /* Used in #postgres_get_refresh_commit_coins() to
- retrieve the original coin envelopes, to either be
- verified or signed. */
- PREPARE ("get_refresh_commit_coin",
- "SELECT"
- " coin_ev"
- " FROM refresh_commit_coin"
- " WHERE session_hash=$1 AND newcoin_index=$2",
- 2, NULL);
-
- /* Store information about a /deposit the exchange is to execute.
- Used in #postgres_insert_deposit(). */
- PREPARE ("insert_deposit",
- "INSERT INTO deposits "
- "(coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",timestamp"
- ",refund_deadline"
- ",wire_deadline"
- ",merchant_pub"
- ",h_contract_terms"
- ",h_wire"
- ",coin_sig"
- ",wire"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12);",
- 12, NULL);
-
- /* Used in #postgres_insert_refund() to store refund information */
- PREPARE ("insert_refund",
- "INSERT INTO refunds "
- "(coin_pub "
- ",merchant_pub "
- ",merchant_sig "
- ",h_contract_terms "
- ",rtransaction_id "
- ",amount_with_fee_val "
- ",amount_with_fee_frac "
- ",amount_with_fee_curr "
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8);",
- 8, NULL);
-
- /* Fetch an existing deposit request, used to ensure idempotency
- during /deposit processing. Used in #postgres_have_deposit(). */
- PREPARE ("get_deposit",
- "SELECT"
- " amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",timestamp"
- ",refund_deadline"
- ",wire_deadline"
- ",h_contract_terms"
- ",h_wire"
- " FROM deposits"
- " WHERE ("
- " (coin_pub=$1) AND"
- " (h_contract_terms=$2) AND"
- " (merchant_pub=$3)"
- " )",
- 3, NULL);
-
- /* Fetch deposits with rowid '\geq' the given parameter */
- PREPARE ("audit_get_deposits_incr",
- "SELECT"
- " amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",timestamp"
- ",merchant_pub"
- ",denom.denom_pub"
- ",coin_pub"
- ",coin_sig"
- ",refund_deadline"
- ",wire_deadline"
- ",h_contract_terms"
- ",wire"
- ",done"
- ",deposit_serial_id"
- " FROM deposits"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE ("
- " (deposit_serial_id>=$1)"
- " )"
- " ORDER BY deposit_serial_id ASC",
- 1, NULL);
-
- /* Fetch an existing deposit request.
- Used in #postgres_wire_lookup_deposit_wtid(). */
- PREPARE ("get_deposit_for_wtid",
- "SELECT"
- " amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",denom.fee_deposit_curr"
- ",wire_deadline"
- " FROM deposits"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE ("
- " (coin_pub=$1) AND"
- " (merchant_pub=$2) AND"
- " (h_contract_terms=$3) AND"
- " (h_wire=$4)"
- " )",
- 4, NULL);
-
- /* Used in #postgres_get_ready_deposit() */
- PREPARE ("deposits_get_ready",
- "SELECT"
- " deposit_serial_id"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",denom.fee_deposit_curr"
- ",wire_deadline"
- ",h_contract_terms"
- ",wire"
- ",merchant_pub"
- ",coin_pub"
- " FROM deposits"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE"
- " tiny=false AND"
- " done=false AND"
- " wire_deadline<=$1 AND"
- " refund_deadline<$1"
- " ORDER BY wire_deadline ASC"
- " LIMIT 1",
- 1, NULL);
-
- /* Used in #postgres_iterate_matching_deposits() */
- PREPARE ("deposits_iterate_matching",
- "SELECT"
- " deposit_serial_id"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",denom.fee_deposit_curr"
- ",wire_deadline"
- ",h_contract_terms"
- ",coin_pub"
- " FROM deposits"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE"
- " merchant_pub=$1 AND"
- " h_wire=$2 AND"
- " done=false"
- " ORDER BY wire_deadline ASC"
- " LIMIT " TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR,
- 2, NULL);
-
- /* Used in #postgres_mark_deposit_tiny() */
- PREPARE ("mark_deposit_tiny",
- "UPDATE deposits"
- " SET tiny=true"
- " WHERE deposit_serial_id=$1",
- 1, NULL);
-
- /* Used in #postgres_mark_deposit_done() */
- PREPARE ("mark_deposit_done",
- "UPDATE deposits"
- " SET done=true"
- " WHERE deposit_serial_id=$1",
- 1, NULL);
-
- /* Used in #postgres_test_deposit_done() */
- PREPARE ("test_deposit_done",
- "SELECT done"
- " FROM deposits"
- " WHERE coin_pub=$1"
- " AND merchant_pub=$2"
- " AND h_contract_terms=$3"
- " AND h_wire=$4",
- 5, NULL);
-
- /* Used in #postgres_get_coin_transactions() to obtain information
- about how a coin has been spend with /deposit requests. */
- PREPARE ("get_deposit_with_coin_pub",
- "SELECT"
- " amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",denom.fee_deposit_curr"
- ",timestamp"
- ",refund_deadline"
- ",merchant_pub"
- ",h_contract_terms"
- ",h_wire"
- ",wire"
- ",coin_sig"
- " FROM deposits"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " WHERE coin_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_insert_refresh_out() to store the
- generated signature(s) for future requests, i.e. /refresh/link */
- PREPARE ("insert_refresh_out",
- "INSERT INTO refresh_out "
- "(session_hash"
- ",newcoin_index"
- ",ev_sig"
- ") VALUES "
- "($1, $2, $3)",
- 3, NULL);
-
- /* Used in #postgres_get_refresh_out() to test if the
- generated signature(s) already exists */
- PREPARE ("get_refresh_out",
- "SELECT ev_sig"
- " FROM refresh_out"
- " WHERE session_hash=$1"
- " AND newcoin_index=$2",
- 2, NULL);
-
- /* Used in #postgres_get_link_data_list(). We use the session_hash
- to obtain the "noreveal_index" for that session, and then select the
- corresponding signatures (ev_sig) and the denomination keys from
- the respective tables (namely refresh_melts and refresh_order)
- using the session_hash as the primary filter (on join) and the
- 'noreveal_index' to constrain the selection on the commitment.
- We also want to get the triplet for each of the newcoins, so we
- have another constraint to ensure we get each triplet with
- matching "newcoin_index" values. NOTE: This may return many
- results, both for different sessions and for the different coins
- being exchangeed in the refresh ops. NOTE: There may be more
- efficient ways to express the same query. */
- PREPARE ("get_link",
- "SELECT "
- " ev_sig"
- ",denoms.denom_pub"
- " FROM refresh_sessions"
- " JOIN refresh_order ro USING (session_hash)"
- " JOIN refresh_commit_coin rcc USING (session_hash)"
- " JOIN refresh_out rc USING (session_hash)"
- " JOIN denominations denoms ON (ro.denom_pub_hash =
denoms.denom_pub_hash)"
- " WHERE ro.session_hash=$1"
- " AND ro.newcoin_index=rcc.newcoin_index"
- " AND ro.newcoin_index=rc.newcoin_index",
- 1, NULL);
-
- /* Used in #postgres_get_transfer(). Given the public key of a
- melted coin, we obtain the corresponding encrypted link secret
- and the transfer public key. This is done by first finding
- the session_hash(es) of all sessions the coin was melted into,
- and then constraining the result to the selected "noreveal_index".
- NOTE: This may (in theory) return multiple results, one per session
- that the old coin was melted into. */
- PREPARE ("get_transfer",
- "SELECT transfer_pub,session_hash"
- " FROM refresh_sessions rs"
- " JOIN refresh_transfer_public_key rcl USING (session_hash)"
- " WHERE rs.old_coin_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_lookup_wire_transfer */
- PREPARE ("lookup_transactions",
- "SELECT"
- " aggregation_serial_id"
- ",deposits.h_contract_terms"
- ",deposits.wire"
- ",deposits.h_wire"
- ",deposits.coin_pub"
- ",deposits.merchant_pub"
- ",wire_out.execution_date"
- ",deposits.amount_with_fee_val"
- ",deposits.amount_with_fee_frac"
- ",deposits.amount_with_fee_curr"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",denom.fee_deposit_curr"
- " FROM aggregation_tracking"
- " JOIN deposits USING (deposit_serial_id)"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " JOIN wire_out USING (wtid_raw)"
- " WHERE wtid_raw=$1",
- 1, NULL);
-
- /* Used in #postgres_wire_lookup_deposit_wtid */
- PREPARE ("lookup_deposit_wtid",
- "SELECT"
- " aggregation_tracking.wtid_raw"
- ",wire_out.execution_date"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",amount_with_fee_curr"
- ",denom.fee_deposit_val"
- ",denom.fee_deposit_frac"
- ",denom.fee_deposit_curr"
- " FROM deposits"
- " JOIN aggregation_tracking USING (deposit_serial_id)"
- " JOIN known_coins USING (coin_pub)"
- " JOIN denominations denom USING (denom_pub_hash)"
- " JOIN wire_out USING (wtid_raw)"
- " WHERE coin_pub=$1"
- " AND h_contract_terms=$2"
- " AND h_wire=$3"
- " AND merchant_pub=$4",
- 4, NULL);
-
- /* Used in #postgres_insert_aggregation_tracking */
- PREPARE ("insert_aggregation_tracking",
- "INSERT INTO aggregation_tracking "
- "(deposit_serial_id"
- ",wtid_raw"
- ") VALUES "
- "($1, $2)",
- 2, NULL);
-
- /* Used in #postgres_get_wire_fee() */
- PREPARE ("get_wire_fee",
- "SELECT "
- " start_date"
- ",end_date"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",wire_fee_curr"
- ",master_sig"
- " FROM wire_fee"
- " WHERE wire_method=$1"
- " AND start_date <= $2"
- " AND end_date > $2",
- 2, NULL);
-
- /* Used in #postgres_insert_wire_fee */
- PREPARE ("insert_wire_fee",
- "INSERT INTO wire_fee "
- "(wire_method"
- ",start_date"
- ",end_date"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",wire_fee_curr"
- ",master_sig"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7)",
- 7, NULL);
-
- /* Used in #postgres_store_wire_transfer_out */
- PREPARE ("insert_wire_out",
- "INSERT INTO wire_out "
- "(execution_date"
- ",wtid_raw"
- ",wire_target"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6)",
- 6, NULL);
-
- /* Used in #postgres_wire_prepare_data_insert() to store
- wire transfer information before actually committing it with the bank */
- PREPARE ("wire_prepare_data_insert",
- "INSERT INTO prewire "
- "(type"
- ",buf"
- ") VALUES "
- "($1, $2)",
- 2, NULL);
-
- /* Used in #postgres_wire_prepare_data_mark_finished() */
- PREPARE ("wire_prepare_data_mark_done",
- "UPDATE prewire"
- " SET finished=true"
- " WHERE prewire_uuid=$1",
- 1, NULL);
-
- /* Used in #postgres_wire_prepare_data_get() */
- PREPARE ("wire_prepare_data_get",
- "SELECT"
- " prewire_uuid"
- ",type"
- ",buf"
- " FROM prewire"
- " WHERE finished=false"
- " ORDER BY prewire_uuid ASC"
- " LIMIT 1",
- 0, NULL);
-
- /* Used in #postgres_gc() */
- PREPARE ("gc_prewire",
- "DELETE"
- " FROM prewire"
- " WHERE finished=true",
- 0, NULL);
-
- /* Used in #postgres_select_wire_out_above_serial_id() */
- PREPARE ("audit_get_wire_incr",
- "SELECT"
- " wireout_uuid"
- ",execution_date"
- ",wtid_raw"
- ",wire_target"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- " FROM wire_out"
- " WHERE wireout_uuid>=$1"
- " ORDER BY wireout_uuid ASC",
- 1, NULL);
-
- /* Used in #postgres_insert_payback_request() to store payback
- information */
- PREPARE ("payback_insert",
- "INSERT INTO payback "
- "(reserve_pub"
- ",coin_pub"
- ",coin_sig"
- ",coin_blind"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- ",timestamp"
- ",h_blind_ev"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9)",
- 9, NULL);
-
- /* Used in #postgres_select_payback_above_serial_id() to obtain payback
transactions */
- PREPARE ("payback_get_incr",
- "SELECT"
- " payback_uuid"
- ",timestamp"
- ",reserve_pub"
- ",coin_pub"
- ",coin_sig"
- ",coin_blind"
- ",h_blind_ev"
- ",denoms.denom_pub"
- ",coins.denom_sig"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- " FROM payback"
- " JOIN known_coins coins USING (coin_pub)"
- " JOIN denominations denoms USING (denom_pub_hash)"
- " WHERE payback_uuid>=$1"
- " ORDER BY payback_uuid ASC",
- 1, NULL);
-
+ struct GNUNET_PQ_PreparedStatement ps[] = {
+ /* Used in #postgres_insert_denomination_info() */
+ GNUNET_PQ_make_prepare ("denomination_insert",
+ "INSERT INTO denominations "
+ "(denom_pub_hash"
+ ",denom_pub"
+ ",master_pub"
+ ",master_sig"
+ ",valid_from"
+ ",expire_withdraw"
+ ",expire_deposit"
+ ",expire_legal"
+ ",coin_val" /* value of this denom */
+ ",coin_frac" /* fractional value of this denom */
+ ",coin_curr" /* assuming same currency for fees */
+ ",fee_withdraw_val"
+ ",fee_withdraw_frac"
+ ",fee_withdraw_curr" /* must match coin_curr */
+ ",fee_deposit_val"
+ ",fee_deposit_frac"
+ ",fee_deposit_curr" /* must match coin_curr */
+ ",fee_refresh_val"
+ ",fee_refresh_frac"
+ ",fee_refresh_curr" /* must match coin_curr */
+ ",fee_refund_val"
+ ",fee_refund_frac"
+ ",fee_refund_curr" /* must match coin_curr */
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
+ " $11, $12, $13, $14, $15, $16, $17, $18,"
+ " $19, $20, $21, $22, $23);",
+ 23),
+ /* Used in #postgres_get_denomination_info() */
+ GNUNET_PQ_make_prepare ("denomination_get",
+ "SELECT"
+ " master_pub"
+ ",master_sig"
+ ",valid_from"
+ ",expire_withdraw"
+ ",expire_deposit"
+ ",expire_legal"
+ ",coin_val" /* value of this denom */
+ ",coin_frac" /* fractional value of this denom */
+ ",coin_curr" /* assuming same currency for fees */
+ ",fee_withdraw_val"
+ ",fee_withdraw_frac"
+ ",fee_withdraw_curr" /* must match coin_curr */
+ ",fee_deposit_val"
+ ",fee_deposit_frac"
+ ",fee_deposit_curr" /* must match coin_curr */
+ ",fee_refresh_val"
+ ",fee_refresh_frac"
+ ",fee_refresh_curr" /* must match coin_curr */
+ ",fee_refund_val"
+ ",fee_refund_frac"
+ ",fee_refund_curr" /* must match coin_curr */
+ " FROM denominations"
+ " WHERE denom_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_denomination_revocation() */
+ GNUNET_PQ_make_prepare ("denomination_revocation_insert",
+ "INSERT INTO denomination_revocations "
+ "(denom_pub_hash"
+ ",master_sig"
+ ") VALUES "
+ "($1, $2);",
+ 2),
+ /* Used in #postgres_get_denomination_revocation() */
+ GNUNET_PQ_make_prepare ("denomination_revocation_get",
+ "SELECT"
+ " master_sig"
+ ",denom_revocations_serial_id"
+ " FROM denomination_revocations"
+ " WHERE denom_pub_hash=$1;",
+ 1),
+ /* Used in #postgres_reserve_get() */
+ GNUNET_PQ_make_prepare ("reserve_get",
+ "SELECT"
+ " current_balance_val"
+ ",current_balance_frac"
+ ",current_balance_curr"
+ ",expiration_date"
+ " FROM reserves"
+ " WHERE reserve_pub=$1"
+ " LIMIT 1;",
+ 1),
+ /* Used in #postgres_reserves_in_insert() when the reserve is new */
+ GNUNET_PQ_make_prepare ("reserve_create",
+ "INSERT INTO reserves "
+ "(reserve_pub"
+ ",account_details"
+ ",current_balance_val"
+ ",current_balance_frac"
+ ",current_balance_curr"
+ ",expiration_date"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6);",
+ 6),
+ /* Used in #postgres_insert_reserve_closed() */
+ GNUNET_PQ_make_prepare ("reserves_close_insert",
+ "INSERT INTO reserves_close "
+ "(reserve_pub"
+ ",execution_date"
+ ",wtid"
+ ",receiver_account"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ",closing_fee_val"
+ ",closing_fee_frac"
+ ",closing_fee_curr"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
+ 10),
+ /* Used in #postgres_reserves_update() when the reserve is updated */
+ GNUNET_PQ_make_prepare ("reserve_update",
+ "UPDATE reserves"
+ " SET"
+ " expiration_date=$1 "
+ ",current_balance_val=$2 "
+ ",current_balance_frac=$3"
+ " WHERE current_balance_curr=$4"
+ " AND reserve_pub=$5;",
+ 5),
+ /* Used in #postgres_reserves_in_insert() to store transaction details */
+ GNUNET_PQ_make_prepare ("reserves_in_add_transaction",
+ "INSERT INTO reserves_in "
+ "(reserve_pub"
+ ",wire_reference"
+ ",credit_val"
+ ",credit_frac"
+ ",credit_curr"
+ ",sender_account_details"
+ ",execution_date"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7);",
+ 7),
+ /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
+ transactions for reserves with serial id '\geq' the given parameter */
+ GNUNET_PQ_make_prepare ("reserves_in_get_latest_wire_reference",
+ "SELECT"
+ " wire_reference"
+ " FROM reserves_in"
+ " ORDER BY reserve_in_serial_id DESC"
+ " LIMIT 1;",
+ 0),
+ /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
+ transactions for reserves with serial id '\geq' the given parameter */
+ GNUNET_PQ_make_prepare ("audit_reserves_in_get_transactions_incr",
+ "SELECT"
+ " reserve_pub"
+ ",wire_reference"
+ ",credit_val"
+ ",credit_frac"
+ ",credit_curr"
+ ",execution_date"
+ ",sender_account_details"
+ ",reserve_in_serial_id"
+ " FROM reserves_in"
+ " WHERE reserve_in_serial_id>=$1"
+ " ORDER BY reserve_in_serial_id;",
+ 1),
+ /* Used in #postgres_get_reserve_history() to obtain inbound transactions
+ for a reserve */
+ GNUNET_PQ_make_prepare ("reserves_in_get_transactions",
+ "SELECT"
+ " wire_reference"
+ ",credit_val"
+ ",credit_frac"
+ ",credit_curr"
+ ",execution_date"
+ ",sender_account_details"
+ " FROM reserves_in"
+ " WHERE reserve_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_withdraw_info() to store
+ the signature of a blinded coin with the blinded coin's
+ details before returning it during /reserve/withdraw. We store
+ the coin's denomination information (public key, signature)
+ and the blinded message as well as the reserve that the coin
+ is being withdrawn from and the signature of the message
+ authorizing the withdrawal. */
+ GNUNET_PQ_make_prepare ("insert_withdraw_info",
+ "INSERT INTO reserves_out "
+ "(h_blind_ev"
+ ",denom_pub_hash"
+ ",denom_sig"
+ ",reserve_pub"
+ ",reserve_sig"
+ ",execution_date"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
+ 9),
+ /* Used in #postgres_get_withdraw_info() to
+ locate the response for a /reserve/withdraw request
+ using the hash of the blinded message. Used to
+ make sure /reserve/withdraw requests are idempotent. */
+ GNUNET_PQ_make_prepare ("get_withdraw_info",
+ "SELECT"
+ " denom.denom_pub"
+ ",denom_sig"
+ ",reserve_sig"
+ ",reserve_pub"
+ ",execution_date"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_withdraw_val"
+ ",denom.fee_withdraw_frac"
+ ",denom.fee_withdraw_curr"
+ " FROM reserves_out"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE h_blind_ev=$1;",
+ 1),
+ /* Used during #postgres_get_reserve_history() to
+ obtain all of the /reserve/withdraw operations that
+ have been performed on a given reserve. (i.e. to
+ demonstrate double-spending) */
+ GNUNET_PQ_make_prepare ("get_reserves_out",
+ "SELECT"
+ " h_blind_ev"
+ ",denom.denom_pub"
+ ",denom_sig"
+ ",reserve_sig"
+ ",execution_date"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_withdraw_val"
+ ",denom.fee_withdraw_frac"
+ ",denom.fee_withdraw_curr"
+ " FROM reserves_out"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE reserve_pub=$1;",
+ 1),
+ /* Used in #postgres_select_reserves_out_above_serial_id() */
+ GNUNET_PQ_make_prepare ("audit_get_reserves_out_incr",
+ "SELECT"
+ " h_blind_ev"
+ ",denom.denom_pub"
+ ",denom_sig"
+ ",reserve_sig"
+ ",reserve_pub"
+ ",execution_date"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",reserve_out_serial_id"
+ " FROM reserves_out"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE reserve_out_serial_id>=$1"
+ " ORDER BY reserve_out_serial_id ASC;",
+ 1),
+ /* Used in #postgres_get_refresh_session() to fetch
+ high-level information about a refresh session */
+ GNUNET_PQ_make_prepare ("get_refresh_session",
+ "SELECT"
+ " old_coin_pub"
+ ",old_coin_sig"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_refresh_val "
+ ",denom.fee_refresh_frac "
+ ",denom.fee_refresh_curr "
+ ",num_newcoins"
+ ",noreveal_index"
+ " FROM refresh_sessions"
+ " JOIN known_coins"
+ " ON (refresh_sessions.old_coin_pub =
known_coins.coin_pub)"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE session_hash=$1;",
+ 1),
+ /* Used in #postgres_select_refreshs_above_serial_id() to fetch
+ refresh session with id '\geq' the given parameter */
+ GNUNET_PQ_make_prepare ("audit_get_refresh_sessions_incr",
+ "SELECT"
+ " denom.denom_pub"
+ ",old_coin_pub"
+ ",old_coin_sig"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",num_newcoins"
+ ",noreveal_index"
+ ",melt_serial_id"
+ ",session_hash"
+ " FROM refresh_sessions"
+ " JOIN known_coins kc"
+ " ON (refresh_sessions.old_coin_pub =
kc.coin_pub)"
+ " JOIN denominations denom"
+ " ON (kc.denom_pub_hash =
denom.denom_pub_hash)"
+ " WHERE melt_serial_id>=$1"
+ " ORDER BY melt_serial_id ASC;",
+ 1),
+ /* Used in #postgres_create_refresh_session() to store
+ high-level information about a refresh session */
+ GNUNET_PQ_make_prepare ("insert_refresh_session",
+ "INSERT INTO refresh_sessions "
+ "(session_hash "
+ ",old_coin_pub "
+ ",old_coin_sig "
+ ",amount_with_fee_val "
+ ",amount_with_fee_frac "
+ ",amount_with_fee_curr "
+ ",num_newcoins "
+ ",noreveal_index "
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8);",
+ 8),
+ /* Used in #postgres_get_known_coin() to fetch
+ the denomination public key and signature for
+ a coin known to the exchange. */
+ GNUNET_PQ_make_prepare ("get_known_coin",
+ "SELECT"
+ " denom.denom_pub"
+ ",denom_sig"
+ " FROM known_coins"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE coin_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_known_coin() to store
+ the denomination public key and signature for
+ a coin known to the exchange. */
+ GNUNET_PQ_make_prepare ("insert_known_coin",
+ "INSERT INTO known_coins "
+ "(coin_pub"
+ ",denom_pub_hash"
+ ",denom_sig"
+ ") VALUES "
+ "($1,$2,$3);",
+ 3),
+ /* Store information about the desired denominations for a
+ refresh operation, used in #postgres_insert_refresh_order() */
+ GNUNET_PQ_make_prepare ("insert_refresh_order",
+ "INSERT INTO refresh_order "
+ "(newcoin_index "
+ ",session_hash "
+ ",denom_pub_hash "
+ ") VALUES "
+ "($1, $2, $3);",
+ 3),
+ /* Obtain information about the desired denominations for a
+ refresh operation, used in #postgres_get_refresh_order() */
+ GNUNET_PQ_make_prepare ("get_refresh_order",
+ "SELECT denom_pub"
+ " FROM refresh_order"
+ " JOIN denominations denom "
+ " USING (denom_pub_hash)"
+ " WHERE session_hash=$1"
+ " AND newcoin_index=$2;",
+ 2),
+ /* Query the 'refresh_sessions' by coin public key */
+ GNUNET_PQ_make_prepare ("get_refresh_session_by_coin",
+ "SELECT"
+ " session_hash"
+ ",old_coin_sig"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_refresh_val "
+ ",denom.fee_refresh_frac "
+ ",denom.fee_refresh_curr "
+ " FROM refresh_sessions"
+ " JOIN known_coins "
+ " ON (refresh_sessions.old_coin_pub =
known_coins.coin_pub)"
+ " JOIN denominations denom USING
(denom_pub_hash)"
+ " WHERE old_coin_pub=$1;",
+ 1),
+ /* Fetch refunds with rowid '\geq' the given parameter */
+ GNUNET_PQ_make_prepare ("audit_get_refunds_incr",
+ "SELECT"
+ " merchant_pub"
+ ",merchant_sig"
+ ",h_contract_terms"
+ ",rtransaction_id"
+ ",denom.denom_pub"
+ ",coin_pub"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",refund_serial_id"
+ " FROM refunds"
+ " JOIN known_coins kc USING (coin_pub)"
+ " JOIN denominations denom ON (kc.denom_pub_hash
= denom.denom_pub_hash)"
+ " WHERE refund_serial_id>=$1"
+ " ORDER BY refund_serial_id ASC;",
+ 1),
+ /* Query the 'refunds' by coin public key */
+ GNUNET_PQ_make_prepare ("get_refunds_by_coin",
+ "SELECT"
+ " merchant_pub"
+ ",merchant_sig"
+ ",h_contract_terms"
+ ",rtransaction_id"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_refund_val "
+ ",denom.fee_refund_frac "
+ ",denom.fee_refund_curr "
+ " FROM refunds"
+ " JOIN known_coins USING (coin_pub)"
+ " JOIN denominations denom USING
(denom_pub_hash)"
+ " WHERE coin_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_transfer_public_key() to
+ store commitments */
+ GNUNET_PQ_make_prepare ("insert_transfer_public_key",
+ "INSERT INTO refresh_transfer_public_key "
+ "(session_hash"
+ ",transfer_pub"
+ ") VALUES "
+ "($1, $2);",
+ 2),
+ /* Used in #postgres_get_refresh_transfer_public_key() to
+ retrieve original commitments during /refresh/reveal */
+ GNUNET_PQ_make_prepare ("get_refresh_transfer_public_key",
+ "SELECT"
+ " transfer_pub"
+ " FROM refresh_transfer_public_key"
+ " WHERE session_hash=$1;",
+ 1),
+ /* Used in #postgres_insert_refresh_commit_coins() to
+ store coin commitments. */
+ GNUNET_PQ_make_prepare ("insert_refresh_commit_coin",
+ "INSERT INTO refresh_commit_coin "
+ "(session_hash"
+ ",newcoin_index"
+ ",coin_ev"
+ ") VALUES "
+ "($1, $2, $3);",
+ 3),
+ /* Used in #postgres_get_refresh_commit_coins() to
+ retrieve the original coin envelopes, to either be
+ verified or signed. */
+ GNUNET_PQ_make_prepare ("get_refresh_commit_coin",
+ "SELECT"
+ " coin_ev"
+ " FROM refresh_commit_coin"
+ " WHERE session_hash=$1 AND newcoin_index=$2;",
+ 2),
+ /* Store information about a /deposit the exchange is to execute.
+ Used in #postgres_insert_deposit(). */
+ GNUNET_PQ_make_prepare ("insert_deposit",
+ "INSERT INTO deposits "
+ "(coin_pub"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",timestamp"
+ ",refund_deadline"
+ ",wire_deadline"
+ ",merchant_pub"
+ ",h_contract_terms"
+ ",h_wire"
+ ",coin_sig"
+ ",wire"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
+ " $11, $12);",
+ 12),
+ /* Used in #postgres_insert_refund() to store refund information */
+ GNUNET_PQ_make_prepare ("insert_refund",
+ "INSERT INTO refunds "
+ "(coin_pub "
+ ",merchant_pub "
+ ",merchant_sig "
+ ",h_contract_terms "
+ ",rtransaction_id "
+ ",amount_with_fee_val "
+ ",amount_with_fee_frac "
+ ",amount_with_fee_curr "
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8);",
+ 8),
+ /* 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"
+ ",amount_with_fee_curr"
+ ",timestamp"
+ ",refund_deadline"
+ ",wire_deadline"
+ ",h_contract_terms"
+ ",h_wire"
+ " FROM deposits"
+ " WHERE ("
+ " (coin_pub=$1)"
+ " AND (h_contract_terms=$2)"
+ " AND (merchant_pub=$3)"
+ " );",
+ 3),
+ /* Fetch deposits with rowid '\geq' the given parameter */
+ GNUNET_PQ_make_prepare ("audit_get_deposits_incr",
+ "SELECT"
+ " amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",timestamp"
+ ",merchant_pub"
+ ",denom.denom_pub"
+ ",coin_pub"
+ ",coin_sig"
+ ",refund_deadline"
+ ",wire_deadline"
+ ",h_contract_terms"
+ ",wire"
+ ",done"
+ ",deposit_serial_id"
+ " FROM deposits"
+ " JOIN known_coins USING (coin_pub)"
+ " JOIN denominations denom USING
(denom_pub_hash)"
+ " WHERE ("
+ " (deposit_serial_id>=$1)"
+ " )"
+ " ORDER BY deposit_serial_id ASC;",
+ 1),
+ /* Fetch an existing deposit request.
+ Used in #postgres_wire_lookup_deposit_wtid(). */
+ GNUNET_PQ_make_prepare ("get_deposit_for_wtid",
+ "SELECT"
+ " amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_deposit_val"
+ ",denom.fee_deposit_frac"
+ ",denom.fee_deposit_curr"
+ ",wire_deadline"
+ " FROM deposits"
+ " JOIN known_coins USING (coin_pub)"
+ " JOIN denominations denom USING
(denom_pub_hash)"
+ " WHERE ("
+ " (coin_pub=$1)"
+ " AND (merchant_pub=$2)"
+ " AND (h_contract_terms=$3)"
+ " AND (h_wire=$4)"
+ " );",
+ 4),
+ /* Used in #postgres_get_ready_deposit() */
+ GNUNET_PQ_make_prepare ("deposits_get_ready",
+ "SELECT"
+ " deposit_serial_id"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_deposit_val"
+ ",denom.fee_deposit_frac"
+ ",denom.fee_deposit_curr"
+ ",wire_deadline"
+ ",h_contract_terms"
+ ",wire"
+ ",merchant_pub"
+ ",coin_pub"
+ " FROM deposits"
+ " JOIN known_coins USING (coin_pub)"
+ " JOIN denominations denom USING
(denom_pub_hash)"
+ " WHERE tiny=false"
+ " AND done=false"
+ " AND wire_deadline<=$1"
+ " AND refund_deadline<$1"
+ " ORDER BY wire_deadline ASC"
+ " LIMIT 1;",
+ 1),
+ /* Used in #postgres_iterate_matching_deposits() */
+ GNUNET_PQ_make_prepare ("deposits_iterate_matching",
+ "SELECT"
+ " deposit_serial_id"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_deposit_val"
+ ",denom.fee_deposit_frac"
+ ",denom.fee_deposit_curr"
+ ",wire_deadline"
+ ",h_contract_terms"
+ ",coin_pub"
+ " FROM deposits"
+ " JOIN known_coins"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE"
+ " merchant_pub=$1 AND"
+ " h_wire=$2 AND"
+ " done=false"
+ " ORDER BY wire_deadline ASC"
+ " LIMIT "
TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR ";",
+ 2),
+ /* Used in #postgres_mark_deposit_tiny() */
+ GNUNET_PQ_make_prepare ("mark_deposit_tiny",
+ "UPDATE deposits"
+ " SET tiny=true"
+ " WHERE deposit_serial_id=$1",
+ 1),
+ /* Used in #postgres_mark_deposit_done() */
+ GNUNET_PQ_make_prepare ("mark_deposit_done",
+ "UPDATE deposits"
+ " SET done=true"
+ " WHERE deposit_serial_id=$1;",
+ 1),
+ /* Used in #postgres_test_deposit_done() */
+ GNUNET_PQ_make_prepare ("test_deposit_done",
+ "SELECT done"
+ " FROM deposits"
+ " WHERE coin_pub=$1"
+ " AND merchant_pub=$2"
+ " AND h_contract_terms=$3"
+ " AND h_wire=$4;",
+ 5),
+ /* Used in #postgres_get_coin_transactions() to obtain information
+ about how a coin has been spend with /deposit requests. */
+ GNUNET_PQ_make_prepare ("get_deposit_with_coin_pub",
+ "SELECT"
+ " amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_deposit_val"
+ ",denom.fee_deposit_frac"
+ ",denom.fee_deposit_curr"
+ ",timestamp"
+ ",refund_deadline"
+ ",merchant_pub"
+ ",h_contract_terms"
+ ",h_wire"
+ ",wire"
+ ",coin_sig"
+ " FROM deposits"
+ " JOIN known_coins"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " WHERE coin_pub=$1;",
+ 1),
+ /* Used in #postgres_insert_refresh_out() to store the
+ generated signature(s) for future requests, i.e. /refresh/link */
+ GNUNET_PQ_make_prepare ("insert_refresh_out",
+ "INSERT INTO refresh_out "
+ "(session_hash"
+ ",newcoin_index"
+ ",ev_sig"
+ ") VALUES "
+ "($1, $2, $3);",
+ 3),
+ /* Used in #postgres_get_refresh_out() to test if the
+ generated signature(s) already exists */
+ GNUNET_PQ_make_prepare ("get_refresh_out",
+ "SELECT ev_sig"
+ " FROM refresh_out"
+ " WHERE session_hash=$1"
+ " AND newcoin_index=$2;",
+ 2),
+ /* Used in #postgres_get_link_data_list(). We use the session_hash
+ to obtain the "noreveal_index" for that session, and then select the
+ corresponding signatures (ev_sig) and the denomination keys from
+ the respective tables (namely refresh_melts and refresh_order)
+ using the session_hash as the primary filter (on join) and the
+ 'noreveal_index' to constrain the selection on the commitment.
+ We also want to get the triplet for each of the newcoins, so we
+ have another constraint to ensure we get each triplet with
+ matching "newcoin_index" values. NOTE: This may return many
+ results, both for different sessions and for the different coins
+ being exchangeed in the refresh ops. NOTE: There may be more
+ efficient ways to express the same query. */
+ GNUNET_PQ_make_prepare ("get_link",
+ "SELECT "
+ " ev_sig"
+ ",denoms.denom_pub"
+ " FROM refresh_sessions"
+ " JOIN refresh_order ro"
+ " USING (session_hash)"
+ " JOIN refresh_commit_coin rcc"
+ " USING (session_hash)"
+ " JOIN refresh_out rc"
+ " USING (session_hash)"
+ " JOIN denominations denoms"
+ " ON (ro.denom_pub_hash =
denoms.denom_pub_hash)"
+ " WHERE ro.session_hash=$1"
+ " AND ro.newcoin_index=rcc.newcoin_index"
+ " AND ro.newcoin_index=rc.newcoin_index;",
+ 1),
+ /* Used in #postgres_get_transfer(). Given the public key of a
+ melted coin, we obtain the corresponding encrypted link secret
+ and the transfer public key. This is done by first finding
+ the session_hash(es) of all sessions the coin was melted into,
+ and then constraining the result to the selected "noreveal_index".
+ NOTE: This may (in theory) return multiple results, one per session
+ that the old coin was melted into. */
+ GNUNET_PQ_make_prepare ("get_transfer",
+ "SELECT transfer_pub,session_hash"
+ " FROM refresh_sessions rs"
+ " JOIN refresh_transfer_public_key rcl"
+ " USING (session_hash)"
+ " WHERE rs.old_coin_pub=$1;",
+ 1),
+ /* Used in #postgres_lookup_wire_transfer */
+ GNUNET_PQ_make_prepare ("lookup_transactions",
+ "SELECT"
+ " aggregation_serial_id"
+ ",deposits.h_contract_terms"
+ ",deposits.wire"
+ ",deposits.h_wire"
+ ",deposits.coin_pub"
+ ",deposits.merchant_pub"
+ ",wire_out.execution_date"
+ ",deposits.amount_with_fee_val"
+ ",deposits.amount_with_fee_frac"
+ ",deposits.amount_with_fee_curr"
+ ",denom.fee_deposit_val"
+ ",denom.fee_deposit_frac"
+ ",denom.fee_deposit_curr"
+ " FROM aggregation_tracking"
+ " JOIN deposits"
+ " USING (deposit_serial_id)"
+ " JOIN known_coins"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " JOIN wire_out"
+ " USING (wtid_raw)"
+ " WHERE wtid_raw=$1;",
+ 1),
+ /* Used in #postgres_wire_lookup_deposit_wtid */
+ GNUNET_PQ_make_prepare ("lookup_deposit_wtid",
+ "SELECT"
+ " aggregation_tracking.wtid_raw"
+ ",wire_out.execution_date"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",denom.fee_deposit_val"
+ ",denom.fee_deposit_frac"
+ ",denom.fee_deposit_curr"
+ " FROM deposits"
+ " JOIN aggregation_tracking"
+ " USING (deposit_serial_id)"
+ " JOIN known_coins"
+ " USING (coin_pub)"
+ " JOIN denominations denom"
+ " USING (denom_pub_hash)"
+ " JOIN wire_out"
+ " USING (wtid_raw)"
+ " WHERE coin_pub=$1"
+ " AND h_contract_terms=$2"
+ " AND h_wire=$3"
+ " AND merchant_pub=$4;",
+ 4),
+ /* Used in #postgres_insert_aggregation_tracking */
+ GNUNET_PQ_make_prepare ("insert_aggregation_tracking",
+ "INSERT INTO aggregation_tracking "
+ "(deposit_serial_id"
+ ",wtid_raw"
+ ") VALUES "
+ "($1, $2);",
+ 2),
+ /* Used in #postgres_get_wire_fee() */
+ GNUNET_PQ_make_prepare ("get_wire_fee",
+ "SELECT "
+ " start_date"
+ ",end_date"
+ ",wire_fee_val"
+ ",wire_fee_frac"
+ ",wire_fee_curr"
+ ",master_sig"
+ " FROM wire_fee"
+ " WHERE wire_method=$1"
+ " AND start_date <= $2"
+ " AND end_date > $2;",
+ 2),
+ /* Used in #postgres_insert_wire_fee */
+ GNUNET_PQ_make_prepare ("insert_wire_fee",
+ "INSERT INTO wire_fee "
+ "(wire_method"
+ ",start_date"
+ ",end_date"
+ ",wire_fee_val"
+ ",wire_fee_frac"
+ ",wire_fee_curr"
+ ",master_sig"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7);",
+ 7),
+ /* Used in #postgres_store_wire_transfer_out */
+ GNUNET_PQ_make_prepare ("insert_wire_out",
+ "INSERT INTO wire_out "
+ "(execution_date"
+ ",wtid_raw"
+ ",wire_target"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6);",
+ 6),
+ /* Used in #postgres_wire_prepare_data_insert() to store
+ wire transfer information before actually committing it with the bank */
+ GNUNET_PQ_make_prepare ("wire_prepare_data_insert",
+ "INSERT INTO prewire "
+ "(type"
+ ",buf"
+ ") VALUES "
+ "($1, $2);",
+ 2),
+ /* Used in #postgres_wire_prepare_data_mark_finished() */
+ GNUNET_PQ_make_prepare ("wire_prepare_data_mark_done",
+ "UPDATE prewire"
+ " SET finished=true"
+ " WHERE prewire_uuid=$1;",
+ 1),
+ /* Used in #postgres_wire_prepare_data_get() */
+ GNUNET_PQ_make_prepare ("wire_prepare_data_get",
+ "SELECT"
+ " prewire_uuid"
+ ",type"
+ ",buf"
+ " FROM prewire"
+ " WHERE finished=false"
+ " ORDER BY prewire_uuid ASC"
+ " LIMIT 1;",
+ 0),
+ /* Used in #postgres_gc() */
+ GNUNET_PQ_make_prepare ("gc_prewire",
+ "DELETE"
+ " FROM prewire"
+ " WHERE finished=true;",
+ 0),
+ /* Used in #postgres_select_wire_out_above_serial_id() */
+ GNUNET_PQ_make_prepare ("audit_get_wire_incr",
+ "SELECT"
+ " wireout_uuid"
+ ",execution_date"
+ ",wtid_raw"
+ ",wire_target"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ " FROM wire_out"
+ " WHERE wireout_uuid>=$1"
+ " ORDER BY wireout_uuid ASC;",
+ 1),
+ /* Used in #postgres_insert_payback_request() to store payback
+ information */
+ GNUNET_PQ_make_prepare ("payback_insert",
+ "INSERT INTO payback "
+ "(reserve_pub"
+ ",coin_pub"
+ ",coin_sig"
+ ",coin_blind"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ",timestamp"
+ ",h_blind_ev"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
+ 9),
+ /* Used in #postgres_select_payback_above_serial_id() to obtain payback
transactions */
+ GNUNET_PQ_make_prepare ("payback_get_incr",
+ "SELECT"
+ " payback_uuid"
+ ",timestamp"
+ ",reserve_pub"
+ ",coin_pub"
+ ",coin_sig"
+ ",coin_blind"
+ ",h_blind_ev"
+ ",denoms.denom_pub"
+ ",coins.denom_sig"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ " FROM payback"
+ " JOIN known_coins coins"
+ " USING (coin_pub)"
+ " JOIN denominations denoms"
+ " USING (denom_pub_hash)"
+ " WHERE payback_uuid>=$1"
+ " ORDER BY payback_uuid ASC;",
+ 1),
/* Used in #postgres_select_reserve_closed_above_serial_id() to
obtain information about closed reserves */
- PREPARE ("reserves_close_get_incr",
- "SELECT"
- " close_uuid"
- ",reserve_pub"
- ",execution_date"
- ",wtid"
- ",receiver_account"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",closing_fee_curr"
- " FROM reserves_close"
- " WHERE close_uuid>=$1"
- " ORDER BY close_uuid ASC",
- 1, NULL);
-
- /* Used in #postgres_get_reserve_history() to obtain payback transactions
- for a reserve */
- PREPARE ("payback_by_reserve",
- "SELECT"
- " coin_pub"
- ",coin_sig"
- ",coin_blind"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- ",timestamp"
- ",denoms.denom_pub"
- ",coins.denom_sig"
- " FROM payback"
- " JOIN known_coins coins USING (coin_pub)"
- " JOIN denominations denoms USING (denom_pub_hash)"
- " WHERE payback.reserve_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_get_reserve_history() */
- PREPARE ("close_by_reserve",
- "SELECT"
- " amount_val"
- ",amount_frac"
- ",amount_curr"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",closing_fee_curr"
- ",execution_date"
- ",receiver_account"
- ",wtid"
- " FROM reserves_close"
- " WHERE reserve_pub=$1;",
- 1, NULL);
-
- /* Used in #postgres_get_expired_reserves() */
- PREPARE ("get_expired_reserves",
- "SELECT"
- " expiration_date"
- ",account_details"
- ",reserve_pub"
- ",current_balance_val"
- ",current_balance_frac"
- ",current_balance_curr"
- " FROM reserves"
- " WHERE expiration_date<=$1"
- " AND (current_balance_val != 0 "
- " OR current_balance_frac != 0);",
- 1, NULL);
-
- /* Used in #postgres_get_coin_transactions() to obtain payback transactions
- for a coin */
- PREPARE ("payback_by_coin",
- "SELECT"
- " payback.reserve_pub"
- ",coin_sig"
- ",coin_blind"
- ",amount_val"
- ",amount_frac"
- ",amount_curr"
- ",timestamp"
- ",denoms.denom_pub"
- ",coins.denom_sig"
- " FROM payback"
- " JOIN known_coins coins USING (coin_pub)"
- " JOIN denominations denoms USING (denom_pub_hash)"
- " WHERE payback.coin_pub=$1",
- 1, NULL);
-
- /* Used in #postgres_get_reserve_by_h_blind() */
- PREPARE ("reserve_by_h_blind",
- "SELECT"
- " reserve_pub"
- " FROM reserves_out"
- " WHERE h_blind_ev=$1"
- " LIMIT 1;",
- 1, NULL);
-
- PREPARE ("gc_denominations",
- "DELETE"
- " FROM denominations"
- " WHERE expire_legal < $1",
- 1, NULL);
- PREPARE ("gc_reserves",
- "DELETE"
- " FROM reserves"
- " WHERE expiration_date < $1"
- " AND current_balance_val = 0"
- " AND current_balance_frac = 0",
- 1, NULL);
+ GNUNET_PQ_make_prepare ("reserves_close_get_incr",
+ "SELECT"
+ " close_uuid"
+ ",reserve_pub"
+ ",execution_date"
+ ",wtid"
+ ",receiver_account"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ",closing_fee_val"
+ ",closing_fee_frac"
+ ",closing_fee_curr"
+ " FROM reserves_close"
+ " WHERE close_uuid>=$1"
+ " ORDER BY close_uuid ASC;",
+ 1),
+ /* Used in #postgres_get_reserve_history() to obtain payback transactions
+ for a reserve */
+ GNUNET_PQ_make_prepare ("payback_by_reserve",
+ "SELECT"
+ " coin_pub"
+ ",coin_sig"
+ ",coin_blind"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ",timestamp"
+ ",denoms.denom_pub"
+ ",coins.denom_sig"
+ " FROM payback"
+ " JOIN known_coins coins"
+ " USING (coin_pub)"
+ " JOIN denominations denoms"
+ " USING (denom_pub_hash)"
+ " WHERE payback.reserve_pub=$1;",
+ 1),
+ /* Used in #postgres_get_reserve_history() */
+ GNUNET_PQ_make_prepare ("close_by_reserve",
+ "SELECT"
+ " amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ",closing_fee_val"
+ ",closing_fee_frac"
+ ",closing_fee_curr"
+ ",execution_date"
+ ",receiver_account"
+ ",wtid"
+ " FROM reserves_close"
+ " WHERE reserve_pub=$1;",
+ 1),
+ /* Used in #postgres_get_expired_reserves() */
+ GNUNET_PQ_make_prepare ("get_expired_reserves",
+ "SELECT"
+ " expiration_date"
+ ",account_details"
+ ",reserve_pub"
+ ",current_balance_val"
+ ",current_balance_frac"
+ ",current_balance_curr"
+ " FROM reserves"
+ " WHERE expiration_date<=$1"
+ " AND (current_balance_val != 0 "
+ " OR current_balance_frac != 0)"
+ " ORDER BY expiration_date ASC"
+ " LIMIT 1;",
+ 1),
+ /* Used in #postgres_get_coin_transactions() to obtain payback transactions
+ for a coin */
+ GNUNET_PQ_make_prepare ("payback_by_coin",
+ "SELECT"
+ " payback.reserve_pub"
+ ",coin_sig"
+ ",coin_blind"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr"
+ ",timestamp"
+ ",denoms.denom_pub"
+ ",coins.denom_sig"
+
+ " FROM payback"
+ " JOIN known_coins coins"
+ " USING (coin_pub)"
+ " JOIN denominations denoms"
+ " USING (denom_pub_hash)"
+ " WHERE payback.coin_pub=$1;",
+ 1),
+ /* Used in #postgres_get_reserve_by_h_blind() */
+ GNUNET_PQ_make_prepare ("reserve_by_h_blind",
+ "SELECT"
+ " reserve_pub"
+ " FROM reserves_out"
+ " WHERE h_blind_ev=$1"
+ " LIMIT 1;",
+ 1),
+ /* used in #postgres_commit */
+ GNUNET_PQ_make_prepare ("do_commit",
+ "COMMIT",
+ 0),
+ GNUNET_PQ_make_prepare ("gc_denominations",
+ "DELETE"
+ " FROM denominations"
+ " WHERE expire_legal < $1;",
+ 1),
+ GNUNET_PQ_make_prepare ("gc_reserves",
+ "DELETE"
+ " FROM reserves"
+ " WHERE expiration_date < $1"
+ " AND current_balance_val = 0"
+ " AND current_balance_frac = 0;",
+ 1),
+ GNUNET_PQ_PREPARED_STATEMENT_END
+ };
- return GNUNET_OK;
-#undef PREPARE
+ return GNUNET_PQ_prepare_statements (db_conn,
+ ps);
}
@@ -1622,8 +1469,11 @@ static void
db_conn_destroy (void *cls)
{
struct TALER_EXCHANGEDB_Session *session = cls;
- PGconn *db_conn = session->conn;
+ PGconn *db_conn;
+ if (NULL == session)
+ return;
+ db_conn = session->conn;
if (NULL != db_conn)
PQfinish (db_conn);
GNUNET_free (session);
@@ -1645,7 +1495,24 @@ postgres_get_session (void *cls)
struct TALER_EXCHANGEDB_Session *session;
if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
- return session;
+ {
+ if (CONNECTION_BAD == PQstatus (session->conn))
+ {
+ /**
+ * Reset the thread-local database-handle. Disconnects from the
+ * DB. Needed after the database server restarts as we need to
+ * properly reconnect. */
+ GNUNET_assert (0 ==
+ pthread_setspecific (pc->db_conn_threadlocal,
+ NULL));
+ PQfinish (session->conn);
+ GNUNET_free (session);
+ }
+ else
+ {
+ return session;
+ }
+ }
db_conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == db_conn)
return NULL;
@@ -1657,7 +1524,6 @@ postgres_get_session (void *cls)
return NULL;
}
session = GNUNET_new (struct TALER_EXCHANGEDB_Session);
- session->state = GNUNET_SYSERR;
session->conn = db_conn;
if (0 != pthread_setspecific (pc->db_conn_threadlocal,
session))
@@ -1695,11 +1561,9 @@ postgres_start (void *cls,
PQerrorMessage (session->conn));
GNUNET_break (0);
PQclear (result);
- session->state = GNUNET_SYSERR;
return GNUNET_SYSERR;
}
PQclear (result);
- session->state = GNUNET_OK;
return GNUNET_OK;
}
@@ -1722,51 +1586,6 @@ postgres_rollback (void *cls,
GNUNET_break (PGRES_COMMAND_OK ==
PQresultStatus (result));
PQclear (result);
- session->state = GNUNET_SYSERR;
-}
-
-
-/**
- * Check the @a result's error code to see what happened.
- * Also logs errors.
- *
- * @param session session used
- * @param result result to check
- * @return #GNUNET_OK if the request/transaction succeeded
- * #GNUNET_NO if it failed but could succeed if retried
- * #GNUNET_SYSERR on hard errors
- */
-static int
-evaluate_pq_result (struct TALER_EXCHANGEDB_Session *session,
- PGresult *result)
-{
- if (PGRES_COMMAND_OK !=
- PQresultStatus (result))
- {
- const char *sqlstate;
-
- sqlstate = PQresultErrorField (result,
- PG_DIAG_SQLSTATE);
- if (NULL == sqlstate)
- {
- /* very unexpected... */
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if ( (0 == strcmp (sqlstate,
- PQ_DIAG_SQLSTATE_DEADLOCK)) ||
- (0 == strcmp (sqlstate,
- PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)) )
- {
- /* These two can be retried and have a fair chance of working
- the next time */
- QUERY_ERR (result, session->conn);
- return GNUNET_NO;
- }
- BREAK_DB_ERR(result, session->conn);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
}
@@ -1775,113 +1594,19 @@ evaluate_pq_result (struct TALER_EXCHANGEDB_Session
*session,
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session the database connection
- * @return #GNUNET_SYSERR on hard error,
- * #GNUNET_NO if commit failed but retry may work,
- * #GNUNET_OK on success
+ * @return final transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_commit (void *cls,
struct TALER_EXCHANGEDB_Session *session)
{
- PGresult *result;
- int ret;
- int state;
-
- state = session->state;
- if (GNUNET_OK != state)
- {
- postgres_rollback (cls,
- session);
- return state;
- }
- result = PQexec (session->conn,
- "COMMIT");
- ret = evaluate_pq_result (session,
- result);
- GNUNET_break (GNUNET_SYSERR != ret);
- PQclear (result);
- return ret;
-}
-
-
-/**
- * Update the @a session state based on the latest @a result from
- * the database. Checks the status code of @a result and possibly
- * sets the state to failed (#GNUNET_SYSERR) or transiently failed
- * (#GNUNET_NO).
- *
- * @param session the session in which the transaction is running
- * @param statement name of the statement we were executing (for logging)
- * @param result the result we got from Postgres
- * @return current session state, i.e.
- * #GNUNET_OK on success
- * #GNUNET_NO if the transaction had a transient failure
- * #GNUNET_SYSERR if the transaction had a hard failure
- */
-static int
-update_session_from_result (struct TALER_EXCHANGEDB_Session *session,
- const char *statement,
- PGresult *result)
-{
- int ret;
-
- if (GNUNET_OK != session->state)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR; /* we already failed, why do we keep going? */
- }
- ret = evaluate_pq_result (session,
- result);
- if (GNUNET_OK == ret)
- return ret;
- GNUNET_log ((GNUNET_NO == ret)
- ? GNUNET_ERROR_TYPE_INFO
- : GNUNET_ERROR_TYPE_ERROR,
- "Statement `%s' failed: %s/%s/%s/%s/%s",
- statement,
- PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY),
- PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL),
- PQresultErrorMessage (result),
- PQresStatus (PQresultStatus (result)),
- PQerrorMessage (session->conn));
- session->state = ret;
- return ret;
-}
-
-
-/**
- * Execute a named prepared @a statement that is NOT a SELECT statement
- * in @a session using the given @a params. Returns the resulting session
- * state.
- *
- * @param session session to execute the statement in
- * @param statement name of the statement
- * @param params parameters to give to the statement
(#GNUNET_PQ_query_param_end-terminated)
- * @return #GNUNET_OK on success
- * #GNUNET_NO if the transaction had a transient failure
- * #GNUNET_SYSERR if the transaction had a hard failure
- */
-static int
-execute_prepared_non_select (struct TALER_EXCHANGEDB_Session *session,
- const char *statement,
- const struct GNUNET_PQ_QueryParam *params)
-{
- PGresult *result;
- int ret;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_end
+ };
- if (GNUNET_OK != session->state)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR; /* we already failed, why keep going? */
- }
- result = GNUNET_PQ_exec_prepared (session->conn,
- statement,
- params);
- ret = update_session_from_result (session,
- statement,
- result);
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "do_commit",
+ params);
}
@@ -1893,9 +1618,9 @@ execute_prepared_non_select (struct
TALER_EXCHANGEDB_Session *session,
* @param session connection to use
* @param denom_pub the public key used for signing coins of this denomination
* @param issue issuing information with value, fees and other info about the
coin
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return status of the query
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_denomination_info (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_DenominationPublicKey
*denom_pub,
@@ -1932,9 +1657,9 @@ postgres_insert_denomination_info (void *cls,
TALER_amount_cmp_currency_nbo (&issue->properties.value,
&issue->properties.fee_refund));
- return execute_prepared_non_select (session,
- "denomination_insert",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "denomination_insert",
+ params);
}
@@ -1944,89 +1669,57 @@ postgres_insert_denomination_info (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param denom_pub the public key used for signing coins of this denomination
- * @param[out] issue set to issue information with value, fees and other info
about the coin, can be NULL
- * @return #GNUNET_OK on success; #GNUNET_NO if no record was found,
#GNUNET_SYSERR on failure
+ * @param[out] issue set to issue information with value, fees and other info
about the coin
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_denomination_info (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_DenominationPublicKey
*denom_pub,
struct
TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
{
- PGresult *result;
+ enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_rsa_public_key (denom_pub->rsa_public_key),
GNUNET_PQ_query_param_end
};
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("master_pub",
+ &issue->properties.master),
+ GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+ &issue->signature),
+ GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from",
+ &issue->properties.start),
+ GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
+
&issue->properties.expire_withdraw),
+ GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit",
+ &issue->properties.expire_deposit),
+ GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal",
+ &issue->properties.expire_legal),
+ TALER_PQ_result_spec_amount_nbo ("coin",
+ &issue->properties.value),
+ TALER_PQ_result_spec_amount_nbo ("fee_withdraw",
+ &issue->properties.fee_withdraw),
+ TALER_PQ_result_spec_amount_nbo ("fee_deposit",
+ &issue->properties.fee_deposit),
+ TALER_PQ_result_spec_amount_nbo ("fee_refresh",
+ &issue->properties.fee_refresh),
+ TALER_PQ_result_spec_amount_nbo ("fee_refund",
+ &issue->properties.fee_refund),
+ GNUNET_PQ_result_spec_end
+ };
- result = GNUNET_PQ_exec_prepared (session->conn,
- "denomination_get",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result,
- session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- if (1 != PQntuples (result))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (NULL == issue)
- {
- PQclear (result);
- return GNUNET_OK;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("master_pub",
- &issue->properties.master),
- GNUNET_PQ_result_spec_auto_from_type ("master_sig",
- &issue->signature),
- GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from",
- &issue->properties.start),
- GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
-
&issue->properties.expire_withdraw),
- GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit",
-
&issue->properties.expire_deposit),
- GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal",
-
&issue->properties.expire_legal),
- TALER_PQ_result_spec_amount_nbo ("coin",
- &issue->properties.value),
- TALER_PQ_result_spec_amount_nbo ("fee_withdraw",
- &issue->properties.fee_withdraw),
- TALER_PQ_result_spec_amount_nbo ("fee_deposit",
- &issue->properties.fee_deposit),
- TALER_PQ_result_spec_amount_nbo ("fee_refresh",
- &issue->properties.fee_refresh),
- TALER_PQ_result_spec_amount_nbo ("fee_refund",
- &issue->properties.fee_refund),
- GNUNET_PQ_result_spec_end
- };
-
- EXITIF (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0));
- }
- PQclear (result);
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "denomination_get",
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ return qs;
issue->properties.purpose.size = htonl (sizeof (struct
TALER_DenominationKeyValidityPS));
issue->properties.purpose.purpose = htonl
(TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
&issue->properties.denom_hash);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (result);
- return GNUNET_SYSERR;
+ return qs;
}
@@ -2038,53 +1731,27 @@ postgres_get_denomination_info (void *cls,
* @param[in,out] reserve the reserve data. The public key of the reserve
should be
* set in this structure; it is used to query the database. The
balance
* and expiration are then filled accordingly.
- * @return #GNUNET_OK upon success;
- * #GNUNET_NO if there were no results (but no hard failure)
- * #GNUNET_SYSERR upon failure
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_reserve_get (void *cls,
struct TALER_EXCHANGEDB_Session *session,
struct TALER_EXCHANGEDB_Reserve *reserve)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type(&reserve->pub),
GNUNET_PQ_query_param_end
};
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount("current_balance", &reserve->balance),
+ GNUNET_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
+ GNUNET_PQ_result_spec_end
+ };
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserve_get",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount("current_balance", &reserve->balance),
- GNUNET_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
- GNUNET_PQ_result_spec_end
- };
-
- EXITIF (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0));
- }
- PQclear (result);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (result);
- return GNUNET_SYSERR;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "reserve_get",
+ params,
+ rs);
}
@@ -2095,11 +1762,9 @@ postgres_reserve_get (void *cls,
* @param session the database connection
* @param reserve the reserve structure whose data will be used to update the
* corresponding record in the database.
- * @return #GNUNET_OK upon successful update;
- * #GNUNET_NO if we failed but should retry the transaction
- * #GNUNET_SYSERR upon any error
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
reserves_update (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Reserve *reserve)
@@ -2111,9 +1776,9 @@ reserves_update (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "reserve_update",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "reserve_update",
+ params);
}
@@ -2130,11 +1795,9 @@ reserves_update (void *cls,
* @param sender_account_details account information for the sender
* @param wire_reference unique reference identifying the wire transfer
(binary blob)
* @param wire_reference_size number of bytes in @a wire_reference
- * @return #GNUNET_OK upon success; #GNUNET_NO if the given
- * @a details are already known for this @a reserve_pub,
- * #GNUNET_SYSERR upon failures (DB error, incompatible currency)
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_reserves_in_insert (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -2145,26 +1808,19 @@ postgres_reserves_in_insert (void *cls,
size_t wire_reference_size)
{
struct PostgresClosure *pg = cls;
- PGresult *result;
- int reserve_exists;
+ enum GNUNET_DB_QueryStatus reserve_exists;
+ enum GNUNET_DB_QueryStatus qs;
struct TALER_EXCHANGEDB_Reserve reserve;
struct GNUNET_TIME_Absolute expiry;
- if (GNUNET_OK !=
- postgres_start (cls,
- session))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
reserve.pub = *reserve_pub;
reserve_exists = postgres_reserve_get (cls,
session,
&reserve);
- if (GNUNET_SYSERR == reserve_exists)
+ if (0 > reserve_exists)
{
GNUNET_break (0);
- goto rollback;
+ return reserve_exists;
}
if ( (0 == reserve.balance.value) &&
(0 == reserve.balance.fraction) )
@@ -2184,9 +1840,14 @@ postgres_reserves_in_insert (void *cls,
*/
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Creating reserve %s with expiration in %s\n",
+ TALER_B2S (reserve_pub),
+ GNUNET_STRINGS_relative_time_to_string
(pg->idle_reserve_expiration_time,
+ GNUNET_NO));
expiry = GNUNET_TIME_absolute_add (execution_time,
pg->idle_reserve_expiration_time);
- if (GNUNET_NO == reserve_exists)
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == reserve_exists)
{
/* New reserve, create balance for the first time; we do this
before adding the actual transaction to "reserves_in", as
@@ -2203,22 +1864,22 @@ postgres_reserves_in_insert (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Reserve does not exist; creating a new one\n");
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserve_create",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus(result))
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "reserve_create",
+ params);
+ if (0 > qs)
+ return qs;
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
- QUERY_ERR (result, session->conn);
- PQclear (result);
- goto rollback;
+ /* Maybe DB did not detect serializiability error already,
+ but clearly there must be one. Still odd. */
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_SOFT_ERROR;
}
- PQclear (result);
}
/* Create new incoming transaction, SQL "primary key" logic
is used to guard against duplicates. If a duplicate is
- detected, we rollback (which really shouldn't undo
- anything) and return #GNUNET_NO to indicate that this failure
- is kind-of harmless (already executed). */
+ detected, we just "succeed" with no changes. */
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
@@ -2230,36 +1891,14 @@ postgres_reserves_in_insert (void *cls,
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserves_in_add_transaction",
- params);
- }
- if (PGRES_COMMAND_OK != PQresultStatus(result))
- {
- const char *efield;
-
- efield = PQresultErrorField (result,
- PG_DIAG_SQLSTATE);
- if ( (PGRES_FATAL_ERROR == PQresultStatus(result)) &&
- (NULL != strstr ("23505", /* unique violation */
- efield)) )
- {
- /* This means we had the same reserve/justification/details
- before */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Uniqueness violation, deposit details already known\n");
- PQclear (result);
- postgres_rollback (cls,
- session);
- return GNUNET_NO;
- }
- QUERY_ERR (result, session->conn);
- PQclear (result);
- goto rollback;
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "reserves_in_add_transaction",
+ params);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ return qs;
}
- PQclear (result);
- if (GNUNET_YES == reserve_exists)
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == reserve_exists)
{
/* If the reserve already existed, we need to still update the
balance; we do this after checking for duplication, as
@@ -2267,7 +1906,7 @@ postgres_reserves_in_insert (void *cls,
back for duplicate transactions; like this, we should virtually
never actually have to rollback anything. */
struct TALER_EXCHANGEDB_Reserve updated_reserve;
-
+
updated_reserve.pub = reserve.pub;
if (GNUNET_OK !=
TALER_amount_add (&updated_reserve.balance,
@@ -2277,31 +1916,15 @@ postgres_reserves_in_insert (void *cls,
/* currency overflow or incompatible currency */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Attempt to deposit incompatible amount into reserve\n");
- goto rollback;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry,
reserve.expiry);
- if (GNUNET_OK !=
- reserves_update (cls,
- session,
- &updated_reserve))
- goto rollback;
- }
- if (GNUNET_OK != postgres_commit (cls,
- session))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to commit transaction adding amount to reserve\n");
- return GNUNET_SYSERR;
+ return reserves_update (cls,
+ session,
+ &updated_reserve);
}
- return GNUNET_OK;
-
- rollback:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Transaction failed, doing rollback\n");
- postgres_rollback (cls,
- session);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -2312,56 +1935,28 @@ postgres_reserves_in_insert (void *cls,
* @param session the database session handle
* @param[out] wire_reference set to unique reference identifying the wire
transfer (binary blob)
* @param[out] wire_reference_size set to number of bytes in @a wire_reference
- * @return #GNUNET_OK upon success; #GNUNET_NO if we never got any incoming
transfers
- * #GNUNET_SYSERR upon failures (DB error)
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_latest_reserve_in_reference (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
void **wire_reference,
size_t *wire_reference_size)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_end
};
- int ret;
-
- ret = GNUNET_SYSERR;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserves_in_get_latest_wire_reference",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- goto cleanup;
- }
- if (0 == PQntuples (result))
- {
- ret = GNUNET_NO;
- goto cleanup;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_variable_size ("wire_reference",
- wire_reference,
- wire_reference_size),
- GNUNET_PQ_result_spec_end
- };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_variable_size ("wire_reference",
+ wire_reference,
+ wire_reference_size),
+ GNUNET_PQ_result_spec_end
+ };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- goto cleanup;
- }
- }
- ret = GNUNET_OK;
- cleanup:
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+
"reserves_in_get_latest_wire_reference",
+ params,
+ rs);
}
@@ -2375,71 +1970,40 @@ postgres_get_latest_reserve_in_reference (void *cls,
* `h_coin_envelope` in the @a collectable to be returned)
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
- * @return #GNUNET_SYSERR on internal error
- * #GNUNET_NO if the collectable was not found
- * #GNUNET_YES on success
+ * @return statement execution status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_withdraw_info (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_blind,
struct TALER_EXCHANGEDB_CollectableBlindcoin
*collectable)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_blind),
GNUNET_PQ_query_param_end
};
- int ret;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+
&collectable->denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+ &collectable->sig.rsa_signature),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+ &collectable->reserve_sig),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &collectable->reserve_pub),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &collectable->amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_withdraw",
+ &collectable->withdraw_fee),
+ GNUNET_PQ_result_spec_end
+ };
- ret = GNUNET_SYSERR;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_withdraw_info",
- params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- goto cleanup;
- }
- if (0 == PQntuples (result))
- {
- ret = GNUNET_NO;
- goto cleanup;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-
&collectable->denom_pub.rsa_public_key),
- GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
- &collectable->sig.rsa_signature),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &collectable->reserve_sig),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &collectable->reserve_pub),
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &collectable->amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_withdraw",
- &collectable->withdraw_fee),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- goto cleanup;
- }
- }
- collectable->h_coin_envelope = *h_blind;
- ret = GNUNET_YES;
-
- cleanup:
- PQclear (result);
- return ret;
-}
+ collectable->h_coin_envelope = *h_blind;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_withdraw_info",
+ params,
+ rs);
+}
/**
@@ -2450,17 +2014,14 @@ postgres_get_withdraw_info (void *cls,
* @param session database connection to use
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
- * @return #GNUNET_SYSERR on internal error
- * #GNUNET_NO if we failed but should retry the transaction
- * #GNUNET_YES on success
+ * @return query execution status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_withdraw_info (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
{
struct PostgresClosure *pg = cls;
- PGresult *result;
struct TALER_EXCHANGEDB_Reserve reserve;
struct GNUNET_HashCode denom_pub_hash;
struct GNUNET_TIME_Absolute now;
@@ -2475,31 +2036,32 @@ postgres_insert_withdraw_info (void *cls,
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
GNUNET_PQ_query_param_end
};
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
now = GNUNET_TIME_absolute_get ();
GNUNET_CRYPTO_rsa_public_key_hash (collectable->denom_pub.rsa_public_key,
&denom_pub_hash);
- result = GNUNET_PQ_exec_prepared (session->conn,
- "insert_withdraw_info",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_withdraw_info",
+ params);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- QUERY_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- PQclear (result);
/* update reserve balance */
reserve.pub = collectable->reserve_pub;
- if (GNUNET_OK != postgres_reserve_get (cls,
- session,
- &reserve))
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ (qs = postgres_reserve_get (cls,
+ session,
+ &reserve)))
{
/* Should have been checked before we got here... */
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
}
if (GNUNET_SYSERR ==
TALER_amount_subtract (&reserve.balance,
@@ -2512,314 +2074,381 @@ postgres_insert_withdraw_info (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Withdrawal from reserve `%s' refused due to balance
missmatch. Retrying.\n",
TALER_B2S (&collectable->reserve_pub));
- return GNUNET_NO;
+ return GNUNET_DB_STATUS_SOFT_ERROR;
}
expiry = GNUNET_TIME_absolute_add (now,
pg->idle_reserve_expiration_time);
reserve.expiry = GNUNET_TIME_absolute_max (expiry,
reserve.expiry);
- ret = reserves_update (cls,
- session,
- &reserve);
- if (GNUNET_SYSERR == ret)
+ qs = reserves_update (cls,
+ session,
+ &reserve);
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
GNUNET_break (0);
- return GNUNET_SYSERR;
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
}
- return ret;
+ return qs;
}
/**
- * Get all of the transaction history associated with the specified
- * reserve.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session connection to use
- * @param reserve_pub public key of the reserve
- * @return known transaction history (NULL if reserve is unknown)
+ * Closure for callbacks invoked via #postgres_get_reserve_history.
*/
-static struct TALER_EXCHANGEDB_ReserveHistory *
-postgres_get_reserve_history (void *cls,
- struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_ReservePublicKeyP
*reserve_pub)
+struct ReserveHistoryContext
{
- PGresult *result;
+
+ /**
+ * Which reserve are we building the history for?
+ */
+ const struct TALER_ReservePublicKeyP *reserve_pub;
+
+ /**
+ * Where we build the history.
+ */
struct TALER_EXCHANGEDB_ReserveHistory *rh;
+
+ /**
+ * Tail of @e rh list.
+ */
struct TALER_EXCHANGEDB_ReserveHistory *rh_tail;
- int rows;
- int ret;
- rh = NULL;
- rh_tail = NULL;
- ret = GNUNET_SYSERR;
- /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
+ /**
+ * Set to #GNUNET_SYSERR on serious internal errors during
+ * the callbacks.
+ */
+ int status;
+};
+
+
+/**
+ * Append and return a fresh element to the reserve
+ * history kept in @a rhc.
+ *
+ * @param rhc where the history is kept
+ * @return the fresh element that was added
+ */
+static struct TALER_EXCHANGEDB_ReserveHistory *
+append_rh (struct ReserveHistoryContext *rhc)
+{
+ struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+ tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
+ if (NULL != rhc->rh_tail)
+ {
+ rhc->rh_tail->next = tail;
+ rhc->rh_tail = tail;
+ }
+ else
+ {
+ rhc->rh_tail = tail;
+ rhc->rh = tail;
+ }
+ return tail;
+}
+
+
+/**
+ * Add bank transfers to result set for #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_bank_to_exchange (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveHistoryContext *rhc = cls;
+
+ while (0 < num_results)
{
struct TALER_EXCHANGEDB_BankTransfer *bt;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_end
- };
+ struct TALER_EXCHANGEDB_ReserveHistory *tail;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserves_in_get_transactions",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
+ bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
{
- QUERY_ERR (result, session->conn);
- goto cleanup;
- }
- if (0 == (rows = PQntuples (result)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Asked to fetch history for an unknown reserve.\n");
- goto cleanup;
- }
- while (0 < rows)
- {
- bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_variable_size ("wire_reference",
- &bt->wire_reference,
- &bt->wire_reference_size),
- TALER_PQ_result_spec_amount ("credit",
- &bt->amount),
- GNUNET_PQ_result_spec_absolute_time ("execution_date",
- &bt->execution_date),
- TALER_PQ_result_spec_json ("sender_account_details",
- &bt->sender_account_details),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --rows))
- {
- GNUNET_break (0);
- GNUNET_free (bt);
- PQclear (result);
- goto cleanup;
- }
- }
- bt->reserve_pub = *reserve_pub;
- if (NULL != rh_tail)
- {
- rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh_tail = rh_tail->next;
- }
- else
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_variable_size ("wire_reference",
+ &bt->wire_reference,
+ &bt->wire_reference_size),
+ TALER_PQ_result_spec_amount ("credit",
+ &bt->amount),
+ GNUNET_PQ_result_spec_absolute_time ("execution_date",
+ &bt->execution_date),
+ TALER_PQ_result_spec_json ("sender_account_details",
+ &bt->sender_account_details),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ --num_results))
{
- rh_tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh = rh_tail;
+ GNUNET_break (0);
+ GNUNET_free (bt);
+ rhc->status = GNUNET_SYSERR;
+ return;
}
- rh_tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
- rh_tail->details.bank = bt;
- } /* end of 'while (0 < rows)' */
- PQclear (result);
- }
- /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_end
- };
-
- GNUNET_assert (NULL != rh);
- GNUNET_assert (NULL != rh_tail);
- GNUNET_assert (NULL == rh_tail->next);
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_reserves_out",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- PQclear (result);
- goto cleanup;
}
- rows = PQntuples (result);
- while (0 < rows)
- {
- struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
+ bt->reserve_pub = *rhc->reserve_pub;
+ tail = append_rh (rhc);
+ tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
+ tail->details.bank = bt;
+ } /* end of 'while (0 < rows)' */
+}
- cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
- &cbc->h_coin_envelope),
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
- &cbc->denom_pub.rsa_public_key),
- GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
- &cbc->sig.rsa_signature),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &cbc->reserve_sig),
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &cbc->amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_withdraw",
- &cbc->withdraw_fee),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --rows))
- {
- GNUNET_break (0);
- GNUNET_free (cbc);
- PQclear (result);
- goto cleanup;
- }
- cbc->reserve_pub = *reserve_pub;
- }
- rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh_tail = rh_tail->next;
- rh_tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
- rh_tail->details.withdraw = cbc;
- } /* end of 'while (0 < rows)' */
- ret = GNUNET_OK;
- PQclear (result);
- }
- /** #TALER_EXCHANGEDB_RO_PAYBACK_COIN */
+/**
+ * Add coin withdrawals to result set for #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_withdraw_coin (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveHistoryContext *rhc = cls;
+
+ while (0 < num_results)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_end
- };
+ struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
+ struct TALER_EXCHANGEDB_ReserveHistory *tail;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "payback_by_reserve",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- goto cleanup;
- }
- rows = PQntuples (result);
- while (0 < rows)
+ cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
{
- struct TALER_EXCHANGEDB_Payback *payback;
-
- payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount",
- &payback->value),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &payback->coin.coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
- &payback->coin_blind),
- GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
- &payback->coin_sig),
- GNUNET_PQ_result_spec_absolute_time ("timestamp",
- &payback->timestamp),
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-
&payback->coin.denom_pub.rsa_public_key),
- GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
-
&payback->coin.denom_sig.rsa_signature),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --rows))
- {
- GNUNET_break (0);
- GNUNET_free (payback);
- PQclear (result);
- goto cleanup;
- }
- }
- payback->reserve_pub = *reserve_pub;
- if (NULL != rh_tail)
- {
- rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh_tail = rh_tail->next;
- }
- else
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
+ &cbc->h_coin_envelope),
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+ &cbc->denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+ &cbc->sig.rsa_signature),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+ &cbc->reserve_sig),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &cbc->amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_withdraw",
+ &cbc->withdraw_fee),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ --num_results))
{
- rh_tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh = rh_tail;
+ GNUNET_break (0);
+ GNUNET_free (cbc);
+ rhc->status = GNUNET_SYSERR;
+ return;
}
- rh_tail->type = TALER_EXCHANGEDB_RO_PAYBACK_COIN;
- rh_tail->details.payback = payback;
- } /* end of 'while (0 < rows)' */
- PQclear (result);
+ }
+ cbc->reserve_pub = *rhc->reserve_pub;
+ tail = append_rh (rhc);
+ tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
+ tail->details.withdraw = cbc;
}
+}
- /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
+/**
+ * Add paybacks to result set for #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_payback (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveHistoryContext *rhc = cls;
+
+ while (0 < num_results)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_end
- };
+ struct TALER_EXCHANGEDB_Payback *payback;
+ struct TALER_EXCHANGEDB_ReserveHistory *tail;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "close_by_reserve",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
+ payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
{
- QUERY_ERR (result, session->conn);
- goto cleanup;
- }
- rows = PQntuples (result);
- while (0 < rows)
- {
- struct TALER_EXCHANGEDB_ClosingTransfer *closing;
-
- closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount",
- &closing->amount),
- TALER_PQ_result_spec_amount ("closing_fee",
- &closing->closing_fee),
- GNUNET_PQ_result_spec_absolute_time ("execution_date",
- &closing->execution_date),
- TALER_PQ_result_spec_json ("receiver_account",
- &closing->receiver_account_details),
- GNUNET_PQ_result_spec_auto_from_type ("wtid",
- &closing->wtid),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --rows))
- {
- GNUNET_break (0);
- GNUNET_free (closing);
- PQclear (result);
- goto cleanup;
- }
- }
- closing->reserve_pub = *reserve_pub;
- if (NULL != rh_tail)
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount",
+ &payback->value),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+ &payback->coin.coin_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+ &payback->coin_blind),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+ &payback->coin_sig),
+ GNUNET_PQ_result_spec_absolute_time ("timestamp",
+ &payback->timestamp),
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+
&payback->coin.denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+
&payback->coin.denom_sig.rsa_signature),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ --num_results))
{
- rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh_tail = rh_tail->next;
+ GNUNET_break (0);
+ GNUNET_free (payback);
+ rhc->status = GNUNET_SYSERR;
+ return;
}
- else
+ }
+ payback->reserve_pub = *rhc->reserve_pub;
+ tail = append_rh (rhc);
+ tail->type = TALER_EXCHANGEDB_RO_PAYBACK_COIN;
+ tail->details.payback = payback;
+ } /* end of 'while (0 < rows)' */
+}
+
+
+/**
+ * Add exchange-to-bank transfers to result set for
+ * #postgres_get_reserve_history.
+ *
+ * @param cls a `struct ReserveHistoryContext *`
+ * @param result SQL result
+ * @param num_results number of rows in @a result
+ */
+static void
+add_exchange_to_bank (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveHistoryContext *rhc = cls;
+
+ while (0 < num_results)
+ {
+ struct TALER_EXCHANGEDB_ClosingTransfer *closing;
+ struct TALER_EXCHANGEDB_ReserveHistory *tail;
+
+ closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer);
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount",
+ &closing->amount),
+ TALER_PQ_result_spec_amount ("closing_fee",
+ &closing->closing_fee),
+ GNUNET_PQ_result_spec_absolute_time ("execution_date",
+ &closing->execution_date),
+ TALER_PQ_result_spec_json ("receiver_account",
+ &closing->receiver_account_details),
+ GNUNET_PQ_result_spec_auto_from_type ("wtid",
+ &closing->wtid),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ --num_results))
{
- rh_tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory);
- rh = rh_tail;
+ GNUNET_break (0);
+ GNUNET_free (closing);
+ rhc->status = GNUNET_SYSERR;
+ return;
}
- rh_tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
- rh_tail->details.closing = closing;
- } /* end of 'while (0 < rows)' */
- PQclear (result);
- }
+ }
+ closing->reserve_pub = *rhc->reserve_pub;
+ tail = append_rh (rhc);
+ tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
+ tail->details.closing = closing;
+ } /* end of 'while (0 < rows)' */
+}
- cleanup:
- if (GNUNET_SYSERR == ret)
+/**
+ * Get all of the transaction history associated with the specified
+ * reserve.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session connection to use
+ * @param reserve_pub public key of the reserve
+ * @param[out] rhp set to known transaction history (NULL if reserve is
unknown)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_reserve_history (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_ReservePublicKeyP
*reserve_pub,
+ struct TALER_EXCHANGEDB_ReserveHistory **rhp)
+{
+ struct ReserveHistoryContext rhc;
+ struct {
+ /**
+ * Name of the prepared statement to run.
+ */
+ const char *statement;
+ /**
+ * Function to use to process the results.
+ */
+ GNUNET_PQ_PostgresResultHandler cb;
+ } work[] = {
+ /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */
+ { "reserves_in_get_transactions",
+ add_bank_to_exchange },
+ /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */
+ { "get_reserves_out",
+ &add_withdraw_coin },
+ /** #TALER_EXCHANGEDB_RO_PAYBACK_COIN */
+ { "payback_by_reserve",
+ &add_payback },
+ /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */
+ { "close_by_reserve",
+ &add_exchange_to_bank },
+ /* List terminator */
+ { NULL,
+ NULL }
+ };
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ rhc.reserve_pub = reserve_pub;
+ rhc.rh = NULL;
+ rhc.rh_tail = NULL;
+ rhc.status = GNUNET_OK;
+ for (unsigned int i=0;NULL != work[i].cb;i++)
+ {
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ work[i].statement,
+ params,
+ work[i].cb,
+ &rhc);
+ if ( (0 > qs) ||
+ (GNUNET_OK != rhc.status) )
+ break;
+ }
+ if ( (qs < 0) ||
+ (rhc.status != GNUNET_OK) )
{
common_free_reserve_history (cls,
- rh);
- rh = NULL;
+ rhc.rh);
+ rhc.rh = NULL;
+ if (qs >= 0)
+ {
+ /* status == SYSERR is a very hard error... */
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ }
}
- return rh;
+ *rhp = rhc.rh;
+ return qs;
}
@@ -2829,11 +2458,11 @@ postgres_get_reserve_history (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
* @param deposit deposit to search for
- * @return #GNUNET_YES if we know this operation,
- * #GNUNET_NO if this exact deposit is unknown to us
- * #GNUNET_SYSERR on DB error
+ * @return 1 if we know this operation,
+ * 0 if this exact deposit is unknown to us,
+ * otherwise transaction error status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_have_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit)
@@ -2844,75 +2473,52 @@ postgres_have_deposit (void *cls,
GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_deposit",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
-
+ struct TALER_EXCHANGEDB_Deposit deposit2;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &deposit2.amount_with_fee),
+ GNUNET_PQ_result_spec_absolute_time ("timestamp",
+ &deposit2.timestamp),
+ GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+ &deposit2.refund_deadline),
+ GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+ &deposit2.wire_deadline),
+ GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+ &deposit2.h_contract_terms),
+ GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+ &deposit2.h_wire),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_deposit",
+ params,
+ rs);
+ if (0 >= qs)
+ return qs;
/* Now we check that the other information in @a deposit
also matches, and if not report inconsistencies. */
- {
- struct TALER_EXCHANGEDB_Deposit deposit2;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &deposit2.amount_with_fee),
- GNUNET_PQ_result_spec_absolute_time ("timestamp",
- &deposit2.timestamp),
- GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
- &deposit2.refund_deadline),
- GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
- &deposit2.wire_deadline),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &deposit2.h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("h_wire",
- &deposit2.h_wire),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
- &deposit2.amount_with_fee)) ||
- (deposit->timestamp.abs_value_us !=
- deposit2.timestamp.abs_value_us) ||
- (deposit->refund_deadline.abs_value_us !=
- deposit2.refund_deadline.abs_value_us) ||
- (0 != memcmp (&deposit->h_contract_terms,
- &deposit2.h_contract_terms,
- sizeof (struct GNUNET_HashCode))) ||
- (0 != memcmp (&deposit->h_wire,
- &deposit2.h_wire,
- sizeof (struct GNUNET_HashCode))) )
- {
- /* Inconsistencies detected! Does not match! (We might want to
- expand the API with a 'get_deposit' function to return the
- original transaction details to be used for an error message
- in the future!) #3838 */
- PQclear (result);
- return GNUNET_NO;
- }
- }
- PQclear (result);
- return GNUNET_YES;
+ if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
+ &deposit2.amount_with_fee)) ||
+ (deposit->timestamp.abs_value_us !=
+ deposit2.timestamp.abs_value_us) ||
+ (deposit->refund_deadline.abs_value_us !=
+ deposit2.refund_deadline.abs_value_us) ||
+ (0 != memcmp (&deposit->h_contract_terms,
+ &deposit2.h_contract_terms,
+ sizeof (struct GNUNET_HashCode))) ||
+ (0 != memcmp (&deposit->h_wire,
+ &deposit2.h_wire,
+ sizeof (struct GNUNET_HashCode))) )
+ {
+ /* Inconsistencies detected! Does not match! (We might want to
+ expand the API with a 'get_deposit' function to return the
+ original transaction details to be used for an error message
+ in the future!) #3838 */
+ return 0; /* Counts as if the transaction was not there */
+ }
+ return 1;
}
@@ -2924,11 +2530,9 @@ postgres_have_deposit (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param rowid identifies the deposit row to modify
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_mark_deposit_tiny (void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t rowid)
@@ -2938,9 +2542,9 @@ postgres_mark_deposit_tiny (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "mark_deposit_tiny",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "mark_deposit_tiny",
+ params);
}
@@ -2951,10 +2555,11 @@ postgres_mark_deposit_tiny (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit the deposit to check
- * @return #GNUNET_YES if is is marked done done, #GNUNET_NO if not,
- * #GNUNET_SYSERR on error (deposit unknown)
+ * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
+ * otherwise transaction error status (incl. deposit unknown)
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_test_deposit_done (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit)
@@ -2966,50 +2571,25 @@ postgres_test_deposit_done (void *cls,
GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "test_deposit_done",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (1 != PQntuples (result))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- {
- uint8_t done = 0;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("done",
- &done),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return (done ? GNUNET_YES : GNUNET_NO);
- }
+ uint8_t done = 0;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("done",
+ &done),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "test_deposit_done",
+ params,
+ rs);
+ if (qs < 0)
+ return qs;
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ return GNUNET_DB_STATUS_HARD_ERROR; /* deposit MUST exist */
+ return (done
+ ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
+ : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS);
}
@@ -3021,11 +2601,9 @@ postgres_test_deposit_done (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param rowid identifies the deposit row to modify
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error,
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_mark_deposit_done (void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t rowid)
@@ -3035,9 +2613,9 @@ postgres_mark_deposit_done (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "mark_deposit_done",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "mark_deposit_done",
+ params);
}
@@ -3050,10 +2628,9 @@ postgres_mark_deposit_done (void *cls,
* @param session connection to the database
* @param deposit_cb function to call for ONE such deposit
* @param deposit_cb_cls closure for @a deposit_cb
- * @return number of rows processed, 0 if none exist,
- * #GNUNET_SYSERR on error
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_ready_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
TALER_EXCHANGEDB_DepositIterator deposit_cb,
@@ -3064,77 +2641,161 @@ postgres_get_ready_deposit (void *cls,
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- unsigned int n;
- int ret;
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount deposit_fee;
+ struct GNUNET_TIME_Absolute wire_deadline;
+ struct GNUNET_HashCode h_contract_terms;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ uint64_t serial_id;
+ json_t *wire;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
+ &serial_id),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_deposit",
+ &deposit_fee),
+ GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+ &wire_deadline),
+ GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+ &h_contract_terms),
+ GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+ &merchant_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+ &coin_pub),
+ TALER_PQ_result_spec_json ("wire",
+ &wire),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "deposits_get_ready",
+ params,
+ rs);
+ if (qs <= 0)
+ return qs;
+ qs = deposit_cb (deposit_cb_cls,
+ serial_id,
+ &merchant_pub,
+ &coin_pub,
+ &amount_with_fee,
+ &deposit_fee,
+ &h_contract_terms,
+ wire_deadline,
+ wire);
+ GNUNET_PQ_cleanup_result (rs);
+ return qs;
+}
- result = GNUNET_PQ_exec_prepared (session->conn,
- "deposits_get_ready",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == (n = PQntuples (result)))
- {
- PQclear (result);
- return 0;
- }
- GNUNET_break (1 == n);
+
+/**
+ * Closure for #match_deposit_cb().
+ */
+struct MatchingDepositContext
+{
+ /**
+ * Function to call for each result
+ */
+ TALER_EXCHANGEDB_DepositIterator deposit_cb;
+
+ /**
+ * Closure for @e deposit_cb.
+ */
+ void *deposit_cb_cls;
+
+ /**
+ * Public key of the merchant against which we are matching.
+ */
+ const struct TALER_MerchantPublicKeyP *merchant_pub;
+
+ /**
+ * Maximum number of results to return.
+ */
+ uint32_t limit;
+
+ /**
+ * Loop counter, actual number of results returned.
+ */
+ unsigned int i;
+
+ /**
+ * Set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function for #postgres_iterate_matching_deposits().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct MatchingDepositContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+match_deposit_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct MatchingDepositContext *mdc = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Found %u/%u matching deposits\n",
+ num_results,
+ mdc->limit);
+ num_results = GNUNET_MIN (num_results,
+ mdc->limit);
+ for (mdc->i=0;mdc->i<num_results;mdc->i++)
{
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_HashCode h_contract_terms;
- struct TALER_MerchantPublicKeyP merchant_pub;
struct TALER_CoinSpendPublicKeyP coin_pub;
uint64_t serial_id;
- json_t *wire;
+ enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
- &serial_id),
+ &serial_id),
TALER_PQ_result_spec_amount ("amount_with_fee",
&amount_with_fee),
TALER_PQ_result_spec_amount ("fee_deposit",
&deposit_fee),
GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
- &wire_deadline),
+ &wire_deadline),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &merchant_pub),
+ &h_contract_terms),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin_pub),
- TALER_PQ_result_spec_json ("wire",
- &wire),
+ &coin_pub),
GNUNET_PQ_result_spec_end
};
-
+
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
- 0))
+ mdc->i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ mdc->status = GNUNET_SYSERR;
+ return;
}
- ret = deposit_cb (deposit_cb_cls,
- serial_id,
- &merchant_pub,
- &coin_pub,
- &amount_with_fee,
- &deposit_fee,
- &h_contract_terms,
- wire_deadline,
- wire);
+ qs = mdc->deposit_cb (mdc->deposit_cb_cls,
+ serial_id,
+ mdc->merchant_pub,
+ &coin_pub,
+ &amount_with_fee,
+ &deposit_fee,
+ &h_contract_terms,
+ wire_deadline,
+ NULL);
GNUNET_PQ_cleanup_result (rs);
- PQclear (result);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ break;
}
- return (GNUNET_OK == ret) ? 1 : 0;
}
@@ -3149,10 +2810,10 @@ postgres_get_ready_deposit (void *cls,
* @param deposit_cb function to call for each deposit
* @param deposit_cb_cls closure for @a deposit_cb
* @param limit maximum number of matching deposits to return
- * @return number of rows processed, 0 if none exist,
- * #GNUNET_SYSERR on error
+ * @return transaction status code, if positive:
+ * number of rows processed, 0 if none exist
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_iterate_matching_deposits (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_wire,
@@ -3166,75 +2827,27 @@ postgres_iterate_matching_deposits (void *cls,
GNUNET_PQ_query_param_auto_from_type (h_wire),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- unsigned int i;
- unsigned int n;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "deposits_iterate_matching",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == (n = PQntuples (result)))
- {
- PQclear (result);
- return 0;
- }
- if (n > limit)
- n = limit;
- for (i=0;i<n;i++)
+ struct MatchingDepositContext mdc;
+ enum GNUNET_DB_QueryStatus qs;
+
+ mdc.deposit_cb = deposit_cb;
+ mdc.deposit_cb_cls = deposit_cb_cls;
+ mdc.merchant_pub = merchant_pub;
+ mdc.limit = limit;
+ mdc.status = GNUNET_OK;
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "deposits_iterate_matching",
+ params,
+ &match_deposit_cb,
+ &mdc);
+ if (GNUNET_OK != mdc.status)
{
- struct TALER_Amount amount_with_fee;
- struct TALER_Amount deposit_fee;
- struct GNUNET_TIME_Absolute wire_deadline;
- struct GNUNET_HashCode h_contract_terms;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- uint64_t serial_id;
- int ret;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
- &serial_id),
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_deposit",
- &deposit_fee),
- GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
- &wire_deadline),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &coin_pub),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- ret = deposit_cb (deposit_cb_cls,
- serial_id,
- merchant_pub,
- &coin_pub,
- &amount_with_fee,
- &deposit_fee,
- &h_contract_terms,
- wire_deadline,
- NULL);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
- PQclear (result);
- return i;
+ if (qs >= 0)
+ return mdc.i;
+ return qs;
}
@@ -3245,66 +2858,31 @@ postgres_iterate_matching_deposits (void *cls,
* @param session the database session handle
* @param coin_pub the public key of the coin to search for
* @param coin_info place holder for the returned coin information object
- * @return #GNUNET_SYSERR upon error; #GNUNET_NO if no coin is found;
#GNUNET_OK
- * if upon succesfullying retrieving the record data info @a
- * coin_info
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
get_known_coin (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_CoinPublicInfo *coin_info)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_end
};
- int nrows;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_known_coin",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == nrows); /* due to primary key */
- if (NULL == coin_info)
- {
- PQclear (result);
- return GNUNET_YES;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-
&coin_info->denom_pub.rsa_public_key),
- GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
-
&coin_info->denom_sig.rsa_signature),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- }
- PQclear (result);
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+ &coin_info->denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+ &coin_info->denom_sig.rsa_signature),
+ GNUNET_PQ_result_spec_end
+ };
+
coin_info->coin_pub = *coin_pub;
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_known_coin",
+ params,
+ rs);
}
@@ -3316,11 +2894,9 @@ get_known_coin (void *cls,
* @param cls plugin closure
* @param session the shared database session
* @param coin_info the public coin info
- * @return #GNUNET_SYSERR upon error;
- * #GNUNET_NO on transient error
- * #GNUNET_OK upon success
+ * @return query result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
insert_known_coin (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_CoinPublicInfo *coin_info)
@@ -3335,9 +2911,53 @@ insert_known_coin (void *cls,
GNUNET_CRYPTO_rsa_public_key_hash (coin_info->denom_pub.rsa_public_key,
&denom_pub_hash);
- return execute_prepared_non_select (session,
- "insert_known_coin",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_known_coin",
+ params);
+}
+
+
+/**
+ * Make sure the given @a coin is known to the database.
+ *
+ * @param cls database connection plugin state
+ * @param session database session
+ * @param coin the coin that must be made known
+ * @return database transaction status, non-negative on success
+ */
+static enum GNUNET_DB_QueryStatus
+ensure_coin_known (struct PostgresClosure *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_CoinPublicInfo *coin)
+{
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_CoinPublicInfo known_coin;
+
+ /* check if the coin is already known */
+ qs = get_known_coin (cls,
+ session,
+ &coin->coin_pub,
+ &known_coin);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* no change! */
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
+ /* if not known, insert it */
+ qs = insert_known_coin (cls,
+ session,
+ coin);
+ if (0 >= qs)
+ {
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ qs = GNUNET_DB_STATUS_HARD_ERROR; /* should be impossible */
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ return qs;
}
@@ -3347,16 +2967,14 @@ insert_known_coin (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session connection to the database
* @param deposit deposit information to store
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit)
{
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
TALER_PQ_query_param_amount (&deposit->amount_with_fee),
@@ -3371,31 +2989,13 @@ postgres_insert_deposit (void *cls,
GNUNET_PQ_query_param_end
};
- /* check if the coin is already known */
- ret = get_known_coin (cls,
- session,
- &deposit->coin.coin_pub,
- NULL);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_NO == ret) /* if not, insert it */
- {
- if (GNUNET_OK !=
- (ret = insert_known_coin (cls,
- session,
- &deposit->coin)))
- {
- GNUNET_break (GNUNET_NO == ret);
- return ret;
- }
- }
-
- return execute_prepared_non_select (session,
- "insert_deposit",
- params);
+ if (0 > (qs = ensure_coin_known (cls,
+ session,
+ &deposit->coin)))
+ return qs;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_deposit",
+ params);
}
@@ -3405,11 +3005,9 @@ postgres_insert_deposit (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param refund refund information to store
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_refund (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund)
@@ -3427,9 +3025,9 @@ postgres_insert_refund (void *cls,
GNUNET_assert (GNUNET_YES ==
TALER_amount_cmp_currency (&refund->refund_amount,
&refund->refund_fee));
- return execute_prepared_non_select (session,
- "insert_refund",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_refund",
+ params);
}
@@ -3439,88 +3037,55 @@ postgres_insert_refund (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database handle to use
* @param session_hash hash over the melt to use to locate the session
- * @param[out] refresh_session where to store the result, can be NULL
- * to just check if the session exists
- * @return #GNUNET_YES on success,
- * #GNUNET_NO if not found,
- * #GNUNET_SYSERR on DB failure
+ * @param[out] refresh_session where to store the result
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_refresh_session (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
struct TALER_EXCHANGEDB_RefreshSession
*refresh_session)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (session_hash),
GNUNET_PQ_query_param_end
};
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint16 ("num_newcoins",
+ &refresh_session->num_newcoins),
+ GNUNET_PQ_result_spec_uint16 ("noreveal_index",
+ &refresh_session->noreveal_index),
+ GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
+ &refresh_session->melt.coin.coin_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
+ &refresh_session->melt.coin_sig),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &refresh_session->melt.amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_refresh",
+ &refresh_session->melt.melt_fee),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refresh_session",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- GNUNET_assert (1 == PQntuples (result));
- if (NULL == refresh_session)
- {
- /* We're done if the caller is only interested in whether the
- * session exists or not */
- PQclear (result);
- return GNUNET_YES;
- }
memset (refresh_session,
0,
sizeof (struct TALER_EXCHANGEDB_RefreshSession));
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint16 ("num_newcoins",
- &refresh_session->num_newcoins),
- GNUNET_PQ_result_spec_uint16 ("noreveal_index",
- &refresh_session->noreveal_index),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
-
&refresh_session->melt.coin.coin_pub),
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
- &refresh_session->melt.coin_sig),
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &refresh_session->melt.amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_refresh",
- &refresh_session->melt.melt_fee),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- }
- PQclear (result);
- if (GNUNET_OK !=
- get_known_coin (cls,
- session,
- &refresh_session->melt.coin.coin_pub,
- &refresh_session->melt.coin))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_refresh_session",
+ params,
+ rs);
+ if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
+ (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ (qs = get_known_coin (cls,
+ session,
+ &refresh_session->melt.coin.coin_pub,
+ &refresh_session->melt.coin)) ) )
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
refresh_session->melt.session_hash = *session_hash;
- return GNUNET_YES;
+ return qs;
}
@@ -3531,11 +3096,9 @@ postgres_get_refresh_session (void *cls,
* @param session database handle to use
* @param session_hash hash over the melt to use to locate the session
* @param refresh_session session data to store
- * @return #GNUNET_YES on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on DB failure
+ * @return query status for the transaction
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_create_refresh_session (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -3550,33 +3113,15 @@ postgres_create_refresh_session (void *cls,
GNUNET_PQ_query_param_uint16 (&refresh_session->noreveal_index),
GNUNET_PQ_query_param_end
};
- int ret;
-
- /* check if the coin is already known */
- ret = get_known_coin (cls,
- session,
- &refresh_session->melt.coin.coin_pub,
- NULL);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_NO == ret) /* if not, insert it */
- {
- if (GNUNET_OK !=
- (ret = insert_known_coin (cls,
- session,
- &refresh_session->melt.coin)))
- {
- GNUNET_break (GNUNET_NO == ret);
- return GNUNET_SYSERR;
- }
- }
-
- return execute_prepared_non_select (session,
- "insert_refresh_session",
- params);
+ enum GNUNET_DB_QueryStatus qs;
+
+ if (0 > (qs = ensure_coin_known (cls,
+ session,
+ &refresh_session->melt.coin)))
+ return qs;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_refresh_session",
+ params);
}
@@ -3589,11 +3134,9 @@ postgres_create_refresh_session (void *cls,
* @param session_hash hash to identify refresh session
* @param num_newcoins number of coins to generate, size of the @a denom_pubs
array
* @param denom_pubs array denominations of the coins to create
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on internal error
+ * @return query status for the transaction
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_order (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -3612,18 +3155,18 @@ postgres_insert_refresh_order (void *cls,
GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
GNUNET_PQ_query_param_end
};
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key,
&denom_pub_hash);
- ret = execute_prepared_non_select (session,
- "insert_refresh_order",
- params);
- if (GNUNET_OK != ret)
- return ret;
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_refresh_order",
+ params);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ return qs;
}
}
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -3655,69 +3198,49 @@ free_dpk_result (struct TALER_DenominationPublicKey
*denom_pubs,
* @param session_hash hash to identify refresh session
* @param num_newcoins size of the array of the @a denom_pubs array
* @param denom_pubs where to store the deomination keys
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_refresh_order (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
uint16_t num_newcoins,
struct TALER_DenominationPublicKey *denom_pubs)
-{
- unsigned int i;
-
- for (i=0;i<(unsigned int) num_newcoins;i++)
+{
+ for (unsigned i=0;i<(unsigned int) num_newcoins;i++)
{
uint16_t newcoin_off = (uint16_t) i;
- PGresult *result;
-
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (session_hash),
- GNUNET_PQ_query_param_uint16 (&newcoin_off),
- GNUNET_PQ_query_param_end
- };
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refresh_order",
- params);
- }
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- free_dpk_result (denom_pubs, i);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (session_hash),
+ GNUNET_PQ_query_param_uint16 (&newcoin_off),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+ &denom_pubs[i].rsa_public_key),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_refresh_order",
+ params,
+ rs);
+ switch (qs)
{
- PQclear (result);
- /* FIXME: may want to distinguish between different error cases! */
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
free_dpk_result (denom_pubs, i);
- return GNUNET_SYSERR;
- }
- GNUNET_assert (1 == PQntuples (result));
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
- &denom_pubs[i].rsa_public_key),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- GNUNET_break (0);
- free_dpk_result (denom_pubs, i);
- return GNUNET_SYSERR;
- }
- PQclear (result);
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ default:
+ GNUNET_break (0);
+ break;
}
}
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -3730,11 +3253,9 @@ postgres_get_refresh_order (void *cls,
* @param session_hash hash to identify refresh session
* @param num_newcoins coin index size of the @a commit_coins array
* @param commit_coins array of coin commitments to store
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_commit_coins (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode
*session_hash,
@@ -3750,15 +3271,15 @@ postgres_insert_refresh_commit_coins (void *cls,
commit_coins[coin_off].coin_ev_size),
GNUNET_PQ_query_param_end
};
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
- ret = execute_prepared_non_select (session,
- "insert_refresh_commit_coin",
- params);
- if (GNUNET_OK != ret)
- return ret;
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_refresh_commit_coin",
+ params);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ return qs;
}
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -3793,11 +3314,9 @@ postgres_free_refresh_commit_coins (void *cls,
* @param session_hash hash to identify refresh session
* @param num_newcoins size of the @a commit_coins array
* @param[out] commit_coins array of coin commitments to return
- * @return #GNUNET_OK on success
- * #GNUNET_NO if not found
- * #GNUNET_SYSERR on error
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_refresh_commit_coins (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -3814,53 +3333,29 @@ postgres_get_refresh_commit_coins (void *cls,
};
void *c_buf;
size_t c_buf_size;
- PGresult *result;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_variable_size ("coin_ev",
+ &c_buf,
+ &c_buf_size),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refresh_commit_coin",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_refresh_commit_coin",
+ params,
+ rs);
+ if (0 >= qs)
{
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
postgres_free_refresh_commit_coins (cls,
i,
commit_coins);
- return GNUNET_SYSERR;
+ return qs;
}
- if (0 == PQntuples (result))
- {
- PQclear (result);
- postgres_free_refresh_commit_coins (cls,
- i,
- commit_coins);
- return GNUNET_NO;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_variable_size ("coin_ev",
- &c_buf,
- &c_buf_size),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_YES !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- postgres_free_refresh_commit_coins (cls,
- i,
- commit_coins);
- return GNUNET_SYSERR;
- }
- }
- PQclear (result);
commit_coins[i].coin_ev = c_buf;
commit_coins[i].coin_ev_size = c_buf_size;
}
- return GNUNET_YES;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -3872,11 +3367,9 @@ postgres_get_refresh_commit_coins (void *cls,
* @param session database connection to use
* @param session_hash hash to identify refresh session
* @param tp transfer public key to store
- * @return #GNUNET_SYSERR on internal error,
- * #GNUNET_NO on transient errors
- * #GNUNET_OK on success
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_transfer_public_key (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
const struct GNUNET_HashCode
*session_hash,
@@ -3888,9 +3381,9 @@ postgres_insert_refresh_transfer_public_key (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "insert_transfer_public_key",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_transfer_public_key",
+ params);
}
@@ -3902,11 +3395,9 @@ postgres_insert_refresh_transfer_public_key (void *cls,
* @param session database connection to use
* @param session_hash hash to identify refresh session
* @param[out] tp information to return
- * @return #GNUNET_SYSERR on internal error,
- * #GNUNET_NO if commitment was not found
- * #GNUNET_OK on success
+ * @return transaction status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_refresh_transfer_public_key (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
const struct GNUNET_HashCode
*session_hash,
@@ -3916,40 +3407,16 @@ postgres_get_refresh_transfer_public_key (void *cls,
GNUNET_PQ_query_param_auto_from_type (session_hash),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refresh_transfer_public_key",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
- tp),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_YES !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- return GNUNET_SYSERR;
- }
- }
- PQclear (result);
- return GNUNET_OK;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
+ tp),
+ GNUNET_PQ_result_spec_end
+ };
+
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+
"get_refresh_transfer_public_key",
+ params,
+ rs);
}
@@ -3963,17 +3430,15 @@ postgres_get_refresh_transfer_public_key (void *cls,
* @param session_hash hash to identify refresh session
* @param newcoin_index coin index
* @param ev_sig coin signature
- * @return #GNUNET_OK on success, #GNUNET_NO if we have no such result
- * #GNUNET_SYSERR on error
+ * @return transaction result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_refresh_out (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
uint16_t newcoin_index,
struct TALER_DenominationSignature *ev_sig)
{
- PGresult *result;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (session_hash),
GNUNET_PQ_query_param_uint16 (&newcoin_index),
@@ -3985,31 +3450,10 @@ postgres_get_refresh_out (void *cls,
GNUNET_PQ_result_spec_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refresh_out",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (1 != PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_refresh_out",
+ params,
+ rs);
}
@@ -4024,11 +3468,9 @@ postgres_get_refresh_out (void *cls,
* @param session_hash hash to identify refresh session
* @param newcoin_index coin index
* @param ev_sig coin signature
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return transaction result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_out (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -4042,52 +3484,45 @@ postgres_insert_refresh_out (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "insert_refresh_out",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_refresh_out",
+ params);
}
/**
- * Obtain the link data of a coin, that is the encrypted link
- * information, the denomination keys and the signatures.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection
- * @param session_hash refresh session to get linkage data for
- * @return all known link data for the session
+ * Closure for #add_ldl().
*/
-static struct TALER_EXCHANGEDB_LinkDataList *
-postgres_get_link_data_list (void *cls,
- struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash)
+struct LinkDataContext
{
+ /**
+ * List we are building.
+ */
struct TALER_EXCHANGEDB_LinkDataList *ldl;
- int nrows;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (session_hash),
- GNUNET_PQ_query_param_end
- };
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_link",
- params);
- ldl = NULL;
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return NULL;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- PQclear (result);
- return NULL;
- }
+ /**
+ * Status, set to #GNUNET_SYSERR on errors,
+ */
+ int status;
+};
- for (int i = nrows-1; i >= 0; i--)
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct LinkDataContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_ldl (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LinkDataContext *ldc = cls;
+
+ for (int i = num_results - 1; i >= 0; i--)
{
struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
struct GNUNET_CRYPTO_RsaSignature *sig;
@@ -4108,71 +3543,98 @@ postgres_get_link_data_list (void *cls,
rs,
i))
{
- PQclear (result);
GNUNET_break (0);
common_free_link_data_list (cls,
- ldl);
+ ldc->ldl);
+ ldc->ldl = NULL;
GNUNET_free (pos);
- return NULL;
+ ldc->status = GNUNET_SYSERR;
+ return;
}
}
- pos->next = ldl;
+ pos->next = ldc->ldl;
pos->denom_pub.rsa_public_key = denom_pub;
pos->ev_sig.rsa_signature = sig;
- ldl = pos;
+ ldc->ldl = pos;
}
- PQclear (result);
- return ldl;
}
/**
- * Obtain shared secret and transfer public key from the public key of
- * the coin. This information and the link information returned by
- * #postgres_get_link_data_list() enable the owner of an old coin to
- * determine the private keys of the new coins after the melt.
+ * Obtain the link data of a coin, that is the encrypted link
+ * information, the denomination keys and the signatures.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
- * @param coin_pub public key of the coin
- * @param tdc function to call for each session the coin was melted into
- * @param tdc_cls closure for @a tdc
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on failure (not found)
- * #GNUNET_SYSERR on internal failure (database issue)
+ * @param session_hash refresh session to get linkage data for
+ * @param[out] ldlp set to all known link data for the session
+ * @return transaction status code
*/
-static int
-postgres_get_transfer (void *cls,
- struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- TALER_EXCHANGEDB_TransferDataCallback tdc,
- void *tdc_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_get_link_data_list (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ struct TALER_EXCHANGEDB_LinkDataList **ldlp)
{
+ struct LinkDataContext ldc;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (session_hash),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- int nrows;
+ enum GNUNET_DB_QueryStatus qs;
+
+ ldc.status = GNUNET_OK;
+ ldc.ldl = NULL;
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "get_link",
+ params,
+ &add_ldl,
+ &ldc);
+ *ldlp = ldc.ldl;
+ if (GNUNET_OK != ldc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_transfer",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- /* no matches found */
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #add_link().
+ */
+struct AddLinkContext
+{
+ /**
+ * Function to call on each result.
+ */
+ TALER_EXCHANGEDB_TransferDataCallback tdc;
+
+ /**
+ * Closure for @e tdc.
+ */
+ void *tdc_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on errors.
+ */
+ int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct AddLinkContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_link (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct AddLinkContext *alc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
struct GNUNET_HashCode session_hash;
struct TALER_TransferPublicKeyP transfer_pub;
@@ -4187,379 +3649,485 @@ postgres_get_transfer (void *cls,
rs,
i))
{
- PQclear (result);
GNUNET_break (0);
- return GNUNET_SYSERR;
+ alc->status = GNUNET_SYSERR;
+ return;
}
- tdc (tdc_cls,
- &session_hash,
- &transfer_pub);
+ alc->tdc (alc->tdc_cls,
+ &session_hash,
+ &transfer_pub);
}
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Compile a list of all (historic) transactions performed
- * with the given coin (/refresh/melt, /deposit and /refund operations).
+ * Obtain shared secret and transfer public key from the public key of
+ * the coin. This information and the link information returned by
+ * #postgres_get_link_data_list() enable the owner of an old coin to
+ * determine the private keys of the new coins after the melt.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
- * @param coin_pub coin to investigate
- * @return list of transactions, NULL if coin is fresh
+ * @param coin_pub public key of the coin
+ * @param tdc function to call for each session the coin was melted into
+ * @param tdc_cls closure for @a tdc
+ * @return statement execution status
*/
-static struct TALER_EXCHANGEDB_TransactionList *
-postgres_get_coin_transactions (void *cls,
- struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_CoinSpendPublicKeyP
*coin_pub)
+static enum GNUNET_DB_QueryStatus
+postgres_get_transfer (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ TALER_EXCHANGEDB_TransferDataCallback tdc,
+ void *tdc_cls)
{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_end
+ };
+ struct AddLinkContext al_ctx;
+ enum GNUNET_DB_QueryStatus qs;
+
+ al_ctx.tdc = tdc;
+ al_ctx.tdc_cls = tdc_cls;
+ al_ctx.status = GNUNET_OK;
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "get_transfer",
+ params,
+ &add_link,
+ &al_ctx);
+ if (GNUNET_OK != al_ctx.status)
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
+
+
+/**
+ * Closure for callbacks called from #postgres_get_coin_transactions()
+ */
+struct CoinHistoryContext
+{
+ /**
+ * Head of the coin's history list.
+ */
struct TALER_EXCHANGEDB_TransactionList *head;
- head = NULL;
- /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+ /**
+ * Public key of the coin we are building the history for.
+ */
+ const struct TALER_CoinSpendPublicKeyP *coin_pub;
+
+ /**
+ * Closure for all callbacks of this database plugin.
+ */
+ void *db_cls;
+
+ /**
+ * Database session we are using.
+ */
+ struct TALER_EXCHANGEDB_Session *session;
+
+ /**
+ * Set to transaction status.
+ */
+ enum GNUNET_DB_QueryStatus status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_deposit (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct CoinHistoryContext *chc = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_end
- };
- int nrows;
- PGresult *result;
+ struct TALER_EXCHANGEDB_Deposit *deposit;
struct TALER_EXCHANGEDB_TransactionList *tl;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_deposit_with_coin_pub",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- PQclear (result);
- goto cleanup;
- }
- nrows = PQntuples (result);
- for (int i = 0; i < nrows; i++)
+ enum GNUNET_DB_QueryStatus qs;
+
+ deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
{
- struct TALER_EXCHANGEDB_Deposit *deposit;
-
- deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &deposit->amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_deposit",
- &deposit->deposit_fee),
- GNUNET_PQ_result_spec_absolute_time ("timestamp",
- &deposit->timestamp),
- GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
- &deposit->refund_deadline),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &deposit->merchant_pub),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &deposit->h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("h_wire",
- &deposit->h_wire),
- TALER_PQ_result_spec_json ("wire",
- &deposit->receiver_wire_account),
- GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
- &deposit->csig),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- GNUNET_free (deposit);
- PQclear (result);
- goto cleanup;
- }
- deposit->coin.coin_pub = *coin_pub;
- }
- tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
- tl->next = head;
- tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
- tl->details.deposit = deposit;
- if (GNUNET_SYSERR == get_known_coin (cls,
- session,
- &deposit->coin.coin_pub,
- &deposit->coin))
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &deposit->amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_deposit",
+ &deposit->deposit_fee),
+ GNUNET_PQ_result_spec_absolute_time ("timestamp",
+ &deposit->timestamp),
+ GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+ &deposit->refund_deadline),
+ GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+ &deposit->merchant_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+ &deposit->h_contract_terms),
+ GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+ &deposit->h_wire),
+ TALER_PQ_result_spec_json ("wire",
+ &deposit->receiver_wire_account),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+ &deposit->csig),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
- GNUNET_break (0);
- GNUNET_free (deposit);
- PQclear (result);
- goto cleanup;
+ GNUNET_break (0);
+ GNUNET_free (deposit);
+ chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
- head = tl;
- continue;
+ deposit->coin.coin_pub = *chc->coin_pub;
}
- PQclear (result);
+ tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+ tl->next = chc->head;
+ tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
+ tl->details.deposit = deposit;
+ qs = get_known_coin (chc->db_cls,
+ chc->session,
+ chc->coin_pub,
+ &deposit->coin);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_free (deposit);
+ chc->status = qs;
+ return;
+ }
+ chc->head = tl;
}
- /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_melt (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct CoinHistoryContext *chc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&coin_pub->eddsa_pub),
- GNUNET_PQ_query_param_end
- };
- int nrows;
- PGresult *result;
+ struct TALER_EXCHANGEDB_RefreshMelt *melt;
struct TALER_EXCHANGEDB_TransactionList *tl;
+ enum GNUNET_DB_QueryStatus qs;
- /* check if the melt records exist and get them */
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refresh_session_by_coin",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- goto cleanup;
- }
- nrows = PQntuples (result);
- for (int i=0;i<nrows;i++)
+ melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
{
- struct TALER_EXCHANGEDB_RefreshMelt *melt;
-
- melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("session_hash",
- &melt->session_hash),
- /* oldcoin_index not needed */
- GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
- &melt->coin_sig),
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &melt->amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_refresh",
- &melt->melt_fee),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- GNUNET_free (melt);
- PQclear (result);
- goto cleanup;
- }
- melt->coin.coin_pub = *coin_pub;
- }
- tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
- tl->next = head;
- tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
- tl->details.melt = melt;
- if (GNUNET_SYSERR == get_known_coin (cls,
- session,
- coin_pub,
- &melt->coin))
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("session_hash",
+ &melt->session_hash),
+ /* oldcoin_index not needed */
+ GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
+ &melt->coin_sig),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &melt->amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_refresh",
+ &melt->melt_fee),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
- GNUNET_break (0);
- GNUNET_free (melt);
- PQclear (result);
- goto cleanup;
+ GNUNET_break (0);
+ GNUNET_free (melt);
+ chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
- head = tl;
- continue;
+ melt->coin.coin_pub = *chc->coin_pub;
}
- PQclear (result);
+ tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+ tl->next = chc->head;
+ tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
+ tl->details.melt = melt;
+ qs = get_known_coin (chc->db_cls,
+ chc->session,
+ chc->coin_pub,
+ &melt->coin);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_free (melt);
+ chc->status = qs;
+ return;
+ }
+ chc->head = tl;
}
- /** #TALER_EXCHANGEDB_TT_REFUND */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_refund (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct CoinHistoryContext *chc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_end
- };
- int nrows;
- PGresult *result;
+ struct TALER_EXCHANGEDB_Refund *refund;
struct TALER_EXCHANGEDB_TransactionList *tl;
+ enum GNUNET_DB_QueryStatus qs;
- /* check if a refund records exist and get them */
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_refunds_by_coin",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
+ refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
{
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- goto cleanup;
- }
- nrows = PQntuples (result);
- for (int i=0;i<nrows;i++)
- {
- struct TALER_EXCHANGEDB_Refund *refund;
-
- refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
- &refund->merchant_pub),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
- &refund->merchant_sig),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &refund->h_contract_terms),
- GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
- &refund->rtransaction_id),
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &refund->refund_amount),
- TALER_PQ_result_spec_amount ("fee_refund",
- &refund->refund_fee),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- GNUNET_free (refund);
- PQclear (result);
- goto cleanup;
- }
- refund->coin.coin_pub = *coin_pub;
- }
- tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
- tl->next = head;
- tl->type = TALER_EXCHANGEDB_TT_REFUND;
- tl->details.refund = refund;
- if (GNUNET_SYSERR ==
- get_known_coin (cls,
- session,
- coin_pub,
- &refund->coin))
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+ &refund->merchant_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
+ &refund->merchant_sig),
+ GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+ &refund->h_contract_terms),
+ GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+ &refund->rtransaction_id),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &refund->refund_amount),
+ TALER_PQ_result_spec_amount ("fee_refund",
+ &refund->refund_fee),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
- GNUNET_break (0);
- GNUNET_free (refund);
- PQclear (result);
- goto cleanup;
+ GNUNET_break (0);
+ GNUNET_free (refund);
+ chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
- head = tl;
- continue;
+ refund->coin.coin_pub = *chc->coin_pub;
}
- PQclear (result);
+ tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+ tl->next = chc->head;
+ tl->type = TALER_EXCHANGEDB_TT_REFUND;
+ tl->details.refund = refund;
+ qs = get_known_coin (chc->db_cls,
+ chc->session,
+ chc->coin_pub,
+ &refund->coin);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_free (refund);
+ chc->status = qs;
+ return;
+ }
+ chc->head = tl;
}
- /** #TALER_EXCHANGEDB_TT_PAYBACK */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_payback (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct CoinHistoryContext *chc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_end
- };
- int nrows;
- PGresult *result;
+ struct TALER_EXCHANGEDB_Payback *payback;
struct TALER_EXCHANGEDB_TransactionList *tl;
-
- /* check if a refund records exist and get them */
- result = GNUNET_PQ_exec_prepared (session->conn,
- "payback_by_coin",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
+
+ payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
{
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- goto cleanup;
- }
- nrows = PQntuples (result);
- for (int i=0;i<nrows;i++)
- {
- struct TALER_EXCHANGEDB_Payback *payback;
-
- payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount",
+ &payback->value),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &payback->reserve_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
+ &payback->coin_blind),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+ &payback->coin_sig),
+ GNUNET_PQ_result_spec_absolute_time ("timestamp",
+ &payback->timestamp),
+ GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+
&payback->coin.denom_pub.rsa_public_key),
+ GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+
&payback->coin.denom_sig.rsa_signature),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
{
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount",
- &payback->value),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &payback->reserve_pub),
- GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
- &payback->coin_blind),
- GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
- &payback->coin_sig),
- GNUNET_PQ_result_spec_absolute_time ("timestamp",
- &payback->timestamp),
- GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-
&payback->coin.denom_pub.rsa_public_key),
- GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
-
&payback->coin.denom_sig.rsa_signature),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- GNUNET_free (payback);
- PQclear (result);
- goto cleanup;
- }
- payback->coin.coin_pub = *coin_pub;
+ GNUNET_break (0);
+ GNUNET_free (payback);
+ chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
}
- tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
- tl->next = head;
- tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
- tl->details.payback = payback;
- head = tl;
- continue;
+ payback->coin.coin_pub = *chc->coin_pub;
}
- PQclear (result);
+ tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+ tl->next = chc->head;
+ tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
+ tl->details.payback = payback;
+ chc->head = tl;
}
-
- return head;
- cleanup:
- if (NULL != head)
- common_free_coin_transaction_list (cls,
- head);
- return NULL;
}
/**
- * Lookup the list of Taler transactions that were aggregated
- * into a wire transfer by the respective @a wtid.
+ * Compile a list of all (historic) transactions performed
+ * with the given coin (/refresh/melt, /deposit and /refund operations).
*
- * @param cls closure
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
- * @param wtid the raw wire transfer identifier we used
- * @param cb function to call on each transaction found
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
- * #GNUNET_NO if we found no results
+ * @param coin_pub coin to investigate
+ * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @return database transaction status
*/
-static int
-postgres_lookup_wire_transfer (void *cls,
- struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_WireTransferIdentifierRawP
*wtid,
- TALER_EXCHANGEDB_WireTransferDataCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_get_coin_transactions (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_CoinSpendPublicKeyP
*coin_pub,
+ struct TALER_EXCHANGEDB_TransactionList **tlp)
{
- PGresult *result;
+ struct CoinHistoryContext chc;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_end
};
- int nrows;
+ enum GNUNET_DB_QueryStatus qs;
+ struct {
+ /**
+ * SQL prepared statement name.
+ */
+ const char *statement;
+
+ /**
+ * Function to call to handle the result(s).
+ */
+ GNUNET_PQ_PostgresResultHandler cb;
+ } work[] = {
+ /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+ { "get_deposit_with_coin_pub",
+ &add_coin_deposit },
+ /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
+ { "get_refresh_session_by_coin",
+ &add_coin_melt },
+ /** #TALER_EXCHANGEDB_TT_REFUND */
+ { "get_refunds_by_coin",
+ &add_coin_refund },
+ /** #TALER_EXCHANGEDB_TT_PAYBACK */
+ { "payback_by_coin",
+ &add_coin_payback },
+ { NULL, NULL }
+ };
- /* check if the melt record exists and get it */
- result = GNUNET_PQ_exec_prepared (session->conn,
- "lookup_transactions",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "lookup_wire_transfer() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
+ chc.head = NULL;
+ chc.status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ chc.coin_pub = coin_pub;
+ chc.session = session;
+ chc.db_cls = cls;
+ for (unsigned int i=0;NULL != work[i].statement; i++)
+ {
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ work[i].statement,
+ params,
+ work[i].cb,
+ &chc);
+ if ( (0 > qs) ||
+ (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != chc.status) )
+ {
+ if (NULL != chc.head)
+ common_free_coin_transaction_list (cls,
+ chc.head);
+ *tlp = NULL;
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != chc.status)
+ qs = chc.status;
+ return qs;
+ }
}
- for (int i=0;i<nrows;i++)
+ *tlp = chc.head;
+ if (NULL == chc.head)
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Closure for #handle_wt_result.
+ */
+struct WireTransferResultContext
+{
+ /**
+ * Function to call on each result.
+ */
+ TALER_EXCHANGEDB_WireTransferDataCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Set to #GNUNET_SYSERR on serious errors.
+ */
+ int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results. Helper function
+ * for #postgres_lookup_wire_transfer().
+ *
+ * @param cls closure of type `struct WireTransferResultContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+handle_wt_result (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct WireTransferResultContext *ctx = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
uint64_t rowid;
struct GNUNET_HashCode h_contract_terms;
@@ -4591,37 +4159,75 @@ postgres_lookup_wire_transfer (void *cls,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ ctx->status = GNUNET_SYSERR;
+ return;
}
t = json_object_get (wire, "type");
if (NULL == t)
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ ctx->status = GNUNET_SYSERR;
+ return;
}
wire_method = json_string_value (t);
if (NULL == wire_method)
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ ctx->status = GNUNET_SYSERR;
+ return;
}
- cb (cb_cls,
- rowid,
- &merchant_pub,
- wire_method,
- &h_wire,
- exec_time,
- &h_contract_terms,
- &coin_pub,
- &amount_with_fee,
- &deposit_fee);
+ ctx->cb (ctx->cb_cls,
+ rowid,
+ &merchant_pub,
+ wire_method,
+ &h_wire,
+ exec_time,
+ &h_contract_terms,
+ &coin_pub,
+ &amount_with_fee,
+ &deposit_fee);
GNUNET_PQ_cleanup_result (rs);
}
- PQclear (result);
- return GNUNET_OK;
+}
+
+
+/**
+ * Lookup the list of Taler transactions that were aggregated
+ * into a wire transfer by the respective @a wtid.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param wtid the raw wire transfer identifier we used
+ * @param cb function to call on each transaction found
+ * @param cb_cls closure for @a cb
+ * @return query status of the transaction
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_wire_transfer (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ const struct TALER_WireTransferIdentifierRawP
*wtid,
+ TALER_EXCHANGEDB_WireTransferDataCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_end
+ };
+ struct WireTransferResultContext ctx;
+ enum GNUNET_DB_QueryStatus qs;
+
+ ctx.cb = cb;
+ ctx.cb_cls = cb_cls;
+ ctx.status = GNUNET_OK;
+ /* check if the melt record exists and get it */
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "lookup_transactions",
+ params,
+ &handle_wt_result,
+ &ctx);
+ if (GNUNET_OK != ctx.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
}
@@ -4638,10 +4244,9 @@ postgres_lookup_wire_transfer (void *cls,
* @param merchant_pub merchant public key
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
- * #GNUNET_NO if nothing was found
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_wire_lookup_deposit_wtid (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode
*h_contract_terms,
@@ -4651,7 +4256,7 @@ postgres_wire_lookup_deposit_wtid (void *cls,
TALER_EXCHANGEDB_TrackTransactionCallback cb,
void *cb_cls)
{
- PGresult *result;
+ enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
@@ -4659,122 +4264,76 @@ postgres_wire_lookup_deposit_wtid (void *cls,
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
GNUNET_PQ_query_param_end
};
- int nrows;
-
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct GNUNET_TIME_Absolute exec_time;
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount deposit_fee;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
+ GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
+ TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
+ TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
+ GNUNET_PQ_result_spec_end
+ };
+
/* check if the melt record exists and get it */
- result = GNUNET_PQ_exec_prepared (session->conn,
- "lookup_deposit_wtid",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "lookup_deposit_wtid",
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
+ cb (cb_cls,
+ &wtid,
+ &amount_with_fee,
+ &deposit_fee,
+ exec_time);
+ return qs;
}
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "lookup_deposit_wtid returned 0 matching rows\n");
- PQclear (result);
+ if (0 > qs)
+ return qs;
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "lookup_deposit_wtid returned 0 matching rows\n");
+ {
/* Check if transaction exists in deposits, so that we just
do not have a WTID yet, if so, do call the CB with a NULL wtid
and return #GNUNET_YES! */
- {
- struct GNUNET_PQ_QueryParam params2[] = {
- GNUNET_PQ_query_param_auto_from_type (coin_pub),
- GNUNET_PQ_query_param_auto_from_type (merchant_pub),
- GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (h_wire),
- GNUNET_PQ_query_param_end
- };
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_deposit_for_wtid",
- params2);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "get_deposit_for_wtid returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
-
- /* Ok, we're aware of the transaction, but it has not yet been
- executed */
- {
- struct GNUNET_TIME_Absolute exec_time;
- struct TALER_Amount amount_with_fee;
- struct TALER_Amount deposit_fee;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
- TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
- GNUNET_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- cb (cb_cls,
- NULL,
- &amount_with_fee,
- &deposit_fee,
- exec_time);
- PQclear (result);
- return GNUNET_YES;
- }
- }
- if (1 != nrows)
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_WireTransferIdentifierRawP wtid;
+ struct GNUNET_PQ_QueryParam params2[] = {
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+ GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+ GNUNET_PQ_query_param_auto_from_type (h_wire),
+ GNUNET_PQ_query_param_end
+ };
struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
- GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
+ struct GNUNET_PQ_ResultSpec rs2[] = {
TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
+ GNUNET_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
GNUNET_PQ_result_spec_end
};
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_deposit_for_wtid",
+ params2,
+ rs2);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ /* Ok, we're aware of the transaction, but it has not yet been
+ executed */
+ cb (cb_cls,
+ NULL,
+ &amount_with_fee,
+ &deposit_fee,
+ exec_time);
+ return qs;
}
- cb (cb_cls,
- &wtid,
- &amount_with_fee,
- &deposit_fee,
- exec_time);
+ return qs;
}
- PQclear (result);
- return GNUNET_OK;
}
@@ -4785,11 +4344,9 @@ postgres_wire_lookup_deposit_wtid (void *cls,
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param deposit_serial_id row in the deposits table for which this is
aggregation data
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient errors
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_aggregation_tracking (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct
TALER_WireTransferIdentifierRawP *wtid,
@@ -4802,9 +4359,9 @@ postgres_insert_aggregation_tracking (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "insert_aggregation_tracking",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_aggregation_tracking",
+ params);
}
@@ -4819,10 +4376,9 @@ postgres_insert_aggregation_tracking (void *cls,
* @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee
* @param[out] master_sig signature over the above by the exchange master key
- * @return #GNUNET_OK on success, #GNUNET_NO if no fee is known
- * #GNUNET_SYSERR on failure
+ * @return status of the transaction
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_wire_fee (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
@@ -4844,42 +4400,11 @@ postgres_get_wire_fee (void *cls,
GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
GNUNET_PQ_result_spec_end
};
- PGresult *result;
- int nrows;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_wire_fee",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- /* no matches found */
- PQclear (result);
- return GNUNET_NO;
- }
- if (1 != nrows)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_wire_fee",
+ params,
+ rs);
}
@@ -4893,11 +4418,9 @@ postgres_get_wire_fee (void *cls,
* @param end_date when does the fee end being valid
* @param wire_fee how high is the wire transfer fee
* @param master_sig signature over the above by the exchange master key
- * @return #GNUNET_OK on success or if the record exists,
- * #GNUNET_NO on transient errors
- * #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_wire_fee (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
@@ -4918,98 +4441,94 @@ postgres_insert_wire_fee (void *cls,
struct TALER_MasterSignatureP sig;
struct GNUNET_TIME_Absolute sd;
struct GNUNET_TIME_Absolute ed;
-
- if (GNUNET_OK ==
- postgres_get_wire_fee (cls,
- session,
- type,
- start_date,
- &sd,
- &ed,
- &wf,
- &sig))
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = postgres_get_wire_fee (cls,
+ session,
+ type,
+ start_date,
+ &sd,
+ &ed,
+ &wf,
+ &sig);
+ if (qs < 0)
+ return qs;
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
if (0 != memcmp (&sig,
master_sig,
sizeof (sig)))
{
GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
if (0 != TALER_amount_cmp (wire_fee,
&wf))
{
GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
if ( (sd.abs_value_us != start_date.abs_value_us) ||
(ed.abs_value_us != end_date.abs_value_us) )
{
GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
/* equal record already exists */
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
- return execute_prepared_non_select (session,
- "insert_wire_fee",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_wire_fee",
+ params);
}
/**
- * Obtain information about expired reserves and their
- * remaining balances.
- *
- * @param cls closure of the plugin
- * @param session database connection
- * @param now timestamp based on which we decide expiration
- * @param rec function to call on expired reserves
- * @param rec_cls closure for @a rec
- * @return #GNUNET_SYSERR on database error
- * #GNUNET_NO if there are no expired non-empty reserves
- * #GNUNET_OK on success
+ * Closure for #reserve_expired_cb().
*/
-static int
-postgres_get_expired_reserves (void *cls,
- struct TALER_EXCHANGEDB_Session *session,
- struct GNUNET_TIME_Absolute now,
- TALER_EXCHANGEDB_ReserveExpiredCallback rec,
- void *rec_cls)
+struct ExpiredReserveContext
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_absolute_time (&now),
- GNUNET_PQ_query_param_end
- };
- PGresult *result;
- int nrows;
+ /**
+ * Function to call for each expired reserve.
+ */
+ TALER_EXCHANGEDB_ReserveExpiredCallback rec;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_expired_reserves",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- /* no matches found */
- PQclear (result);
- return GNUNET_NO;
- }
+ /**
+ * Closure to give to @e rec.
+ */
+ void *rec_cls;
+
+ /**
+ * Set to #GNUNET_SYSERR on error.
+ */
+ int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserve_expired_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ExpiredReserveContext *erc = cls;
+ int ret;
- for (int i=0;i<nrows;i++)
+ ret = GNUNET_OK;
+ for (unsigned int i=0;i<num_results;i++)
{
struct GNUNET_TIME_Absolute exp_date;
json_t *account_details;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount remaining_balance;
- int ret;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_absolute_time ("expiration_date",
&exp_date),
@@ -5027,21 +4546,59 @@ postgres_get_expired_reserves (void *cls,
rs,
i))
{
- PQclear (result);
GNUNET_break (0);
- return GNUNET_SYSERR;
+ ret = GNUNET_SYSERR;
+ break;
}
- ret = rec (rec_cls,
- &reserve_pub,
- &remaining_balance,
- account_details,
- exp_date);
+ ret = erc->rec (erc->rec_cls,
+ &reserve_pub,
+ &remaining_balance,
+ account_details,
+ exp_date);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
- PQclear (result);
- return GNUNET_OK;
+ erc->status = ret;
+}
+
+
+/**
+ * Obtain information about expired reserves and their
+ * remaining balances.
+ *
+ * @param cls closure of the plugin
+ * @param session database connection
+ * @param now timestamp based on which we decide expiration
+ * @param rec function to call on expired reserves
+ * @param rec_cls closure for @a rec
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_expired_reserves (void *cls,
+ struct TALER_EXCHANGEDB_Session *session,
+ struct GNUNET_TIME_Absolute now,
+ TALER_EXCHANGEDB_ReserveExpiredCallback rec,
+ void *rec_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_absolute_time (&now),
+ GNUNET_PQ_query_param_end
+ };
+ struct ExpiredReserveContext ectx;
+ enum GNUNET_DB_QueryStatus qs;
+
+ ectx.rec = rec;
+ ectx.rec_cls = rec_cls;
+ ectx.status = GNUNET_OK;
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "get_expired_reserves",
+ params,
+ &reserve_expired_cb,
+ &ectx);
+ if (GNUNET_OK != ectx.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
}
@@ -5056,11 +4613,9 @@ postgres_get_expired_reserves (void *cls,
* @param wtid wire transfer details
* @param amount_with_fee amount we charged to the reserve
* @param closing_fee how high is the closing fee
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if the record exists or on transient errors
- * #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_reserve_closed (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP
*reserve_pub,
@@ -5081,23 +4636,26 @@ postgres_insert_reserve_closed (void *cls,
GNUNET_PQ_query_param_end
};
int ret;
+ enum GNUNET_DB_QueryStatus qs;
- ret = execute_prepared_non_select (session,
- "reserves_close_insert",
- params);
- if (GNUNET_OK != ret)
- return ret;
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "reserves_close_insert",
+ params);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ return qs;
/* update reserve balance */
reserve.pub = *reserve_pub;
- if (GNUNET_OK !=
- (ret = postgres_reserve_get (cls,
- session,
- &reserve)))
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ (qs = postgres_reserve_get (cls,
+ session,
+ &reserve)))
{
/* Existence should have been checked before we got here... */
- GNUNET_break (GNUNET_NO == ret);
- return ret;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
}
ret = TALER_amount_subtract (&reserve.balance,
&reserve.balance,
@@ -5110,18 +4668,12 @@ postgres_insert_reserve_closed (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Closing of reserve `%s' refused due to balance missmatch.
Retrying.\n",
TALER_B2S (reserve_pub));
- return GNUNET_NO;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
GNUNET_break (GNUNET_NO == ret);
- ret = reserves_update (cls,
- session,
- &reserve);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return ret;
+ return reserves_update (cls,
+ session,
+ &reserve);
}
@@ -5133,9 +4685,9 @@ postgres_insert_reserve_closed (void *cls,
* @param type type of the wire transfer (i.e. "sepa")
* @param buf buffer with wire transfer preparation data
* @param buf_size number of bytes in @a buf
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return query status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_wire_prepare_data_insert (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
@@ -5148,9 +4700,9 @@ postgres_wire_prepare_data_insert (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "wire_prepare_data_insert",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "wire_prepare_data_insert",
+ params);
}
@@ -5160,9 +4712,9 @@ postgres_wire_prepare_data_insert (void *cls,
* @param cls closure
* @param session database connection
* @param rowid which entry to mark as finished
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_wire_prepare_data_mark_finished (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
uint64_t rowid)
@@ -5172,9 +4724,9 @@ postgres_wire_prepare_data_mark_finished (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "wire_prepare_data_mark_done",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "wire_prepare_data_mark_done",
+ params);
}
@@ -5186,76 +4738,46 @@ postgres_wire_prepare_data_mark_finished (void *cls,
* @param session database connection
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_wire_prepare_data_get (void *cls,
struct TALER_EXCHANGEDB_Session *session,
TALER_EXCHANGEDB_WirePreparationIterator cb,
void *cb_cls)
{
- PGresult *result;
+ enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_end
};
+ uint64_t prewire_uuid;
+ char *type;
+ void *buf = NULL;
+ size_t buf_size;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
+ &prewire_uuid),
+ GNUNET_PQ_result_spec_string ("type",
+ &type),
+ GNUNET_PQ_result_spec_variable_size ("buf",
+ &buf,
+ &buf_size),
+ GNUNET_PQ_result_spec_end
+ };
- result = GNUNET_PQ_exec_prepared (session->conn,
- "wire_prepare_data_get",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- if (1 != PQntuples (result))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- {
- uint64_t prewire_uuid;
- char *type;
- void *buf = NULL;
- size_t buf_size;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
- &prewire_uuid),
- GNUNET_PQ_result_spec_string ("type",
- &type),
- GNUNET_PQ_result_spec_variable_size ("buf",
- &buf,
- &buf_size),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- cb (cb_cls,
- prewire_uuid,
- type,
- buf,
- buf_size);
- GNUNET_PQ_cleanup_result (rs);
- }
- PQclear (result);
- return GNUNET_OK;
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "wire_prepare_data_get",
+ params,
+ rs);
+ if (0 >= qs)
+ return qs;
+ cb (cb_cls,
+ prewire_uuid,
+ type,
+ buf,
+ buf_size);
+ GNUNET_PQ_cleanup_result (rs);
+ return qs;
}
@@ -5301,7 +4823,6 @@ postgres_start_deferred_wire_out (void *cls,
return GNUNET_SYSERR;
}
PQclear (result);
- session->state = GNUNET_OK;
return GNUNET_OK;
}
@@ -5315,10 +4836,9 @@ postgres_start_deferred_wire_out (void *cls,
* @param wtid subject of the wire transfer
* @param wire_account details about the receiver account of the wire transfer
* @param amount amount that was transmitted
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_store_wire_transfer_out (void *cls,
struct TALER_EXCHANGEDB_Session *session,
struct GNUNET_TIME_Absolute date,
@@ -5334,9 +4854,9 @@ postgres_store_wire_transfer_out (void *cls,
GNUNET_PQ_query_param_end
};
- return execute_prepared_non_select (session,
- "insert_wire_out",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_wire_out",
+ params);
}
@@ -5361,102 +4881,70 @@ postgres_gc (void *cls)
GNUNET_PQ_query_param_end
};
PGconn *conn;
- PGresult *result;
-
+ int ret;
+
now = GNUNET_TIME_absolute_get ();
conn = GNUNET_PQ_connect (pc->connection_cfg_str);
if (NULL == conn)
return GNUNET_SYSERR;
- if (GNUNET_OK !=
- postgres_prepare (conn))
- {
- PQfinish (conn);
- return GNUNET_SYSERR;
- }
- result = GNUNET_PQ_exec_prepared (conn,
- "gc_prewire",
- params_none);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, conn);
- PQclear (result);
- PQfinish (conn);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- result = GNUNET_PQ_exec_prepared (conn,
- "gc_denominations",
- params_time);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result, conn);
- PQclear (result);
- PQfinish (conn);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- result = GNUNET_PQ_exec_prepared (conn,
- "gc_reserves",
- params_time);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
+ ret = postgres_prepare (conn);
+ if (GNUNET_OK == ret)
{
- BREAK_DB_ERR (result, conn);
- PQclear (result);
- PQfinish (conn);
- return GNUNET_SYSERR;
+ if ( (0 > GNUNET_PQ_eval_prepared_non_select (conn,
+ "gc_prewire",
+ params_none)) ||
+ (0 > GNUNET_PQ_eval_prepared_non_select (conn,
+ "gc_denominations",
+ params_time)) ||
+ (0 > GNUNET_PQ_eval_prepared_non_select (conn,
+ "gc_reserves",
+ params_time)) )
+ ret = GNUNET_SYSERR;
}
- PQclear (result);
PQfinish (conn);
- return GNUNET_OK;
+ return ret;
}
/**
- * Select deposits above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param session database connection
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * Closure for #deposit_serial_helper_cb().
*/
-static int
-postgres_select_deposits_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
- TALER_EXCHANGEDB_DepositCallback cb,
- void *cb_cls)
+struct DepositSerialContext
{
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "audit_get_deposits_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- int nrows;
- int ret;
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_deposits_above_serial_id() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_DepositCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct DepositSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+deposit_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct DepositSerialContext *dsc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
struct TALER_EXCHANGEDB_Deposit deposit;
struct TALER_DenominationPublicKey denom_pub;
@@ -5489,39 +4977,39 @@ postgres_select_deposits_above_serial_id (void *cls,
&rowid),
GNUNET_PQ_result_spec_end
};
+ int ret;
+
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ dsc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- deposit.timestamp,
- &deposit.merchant_pub,
- &denom_pub,
- &deposit.coin.coin_pub,
- &deposit.csig,
- &deposit.amount_with_fee,
- &deposit.h_contract_terms,
- deposit.refund_deadline,
- deposit.wire_deadline,
- deposit.receiver_wire_account,
- done);
+ ret = dsc->cb (dsc->cb_cls,
+ rowid,
+ deposit.timestamp,
+ &deposit.merchant_pub,
+ &denom_pub,
+ &deposit.coin.coin_pub,
+ &deposit.csig,
+ &deposit.amount_with_fee,
+ &deposit.h_contract_terms,
+ deposit.refund_deadline,
+ deposit.wire_deadline,
+ deposit.receiver_wire_account,
+ done);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Select refresh sessions above @a serial_id in monotonically increasing
+ * Select deposits above @a serial_id in monotonically increasing
* order.
*
* @param cls closure
@@ -5529,47 +5017,76 @@ postgres_select_deposits_above_serial_id (void *cls,
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_refreshs_above_serial_id (void *cls,
+static enum GNUNET_DB_QueryStatus
+postgres_select_deposits_above_serial_id (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
uint64_t serial_id,
-
TALER_EXCHANGEDB_RefreshSessionCallback cb,
+ TALER_EXCHANGEDB_DepositCallback cb,
void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- int nrows;
- int i;
- int ret;
+ struct DepositSerialContext dsc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "audit_get_deposits_incr",
+ params,
+ &deposit_serial_helper_cb,
+ &dsc);
+ if (GNUNET_OK != dsc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- result = GNUNET_PQ_exec_prepared (session->conn,
- "audit_get_refresh_sessions_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
+/**
+ * Closure for #refreshs_serial_helper_cb().
+ */
+struct RefreshsSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_RefreshSessionCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_refreshs_above_serial_id() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (i=0;i<nrows;i++)
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct RefreshsSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+refreshs_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct RefreshsSerialContext *rsc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
struct TALER_DenominationPublicKey denom_pub;
struct TALER_CoinSpendPublicKeyP coin_pub;
@@ -5579,7 +5096,6 @@ postgres_select_refreshs_above_serial_id (void *cls,
uint16_t noreveal_index;
uint64_t rowid;
struct GNUNET_HashCode session_hash;
-
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
&denom_pub.rsa_public_key),
@@ -5599,35 +5115,35 @@ postgres_select_refreshs_above_serial_id (void *cls,
&session_hash),
GNUNET_PQ_result_spec_end
};
+ int ret;
+
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ rsc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- &denom_pub,
- &coin_pub,
- &coin_sig,
- &amount_with_fee,
- num_newcoins,
- noreveal_index,
- &session_hash);
+ ret = rsc->cb (rsc->cb_cls,
+ rowid,
+ &denom_pub,
+ &coin_pub,
+ &coin_sig,
+ &amount_with_fee,
+ num_newcoins,
+ noreveal_index,
+ &session_hash);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Select refunds above @a serial_id in monotonically increasing
+ * Select refresh sessions above @a serial_id in monotonically increasing
* order.
*
* @param cls closure
@@ -5635,49 +5151,80 @@ postgres_select_refreshs_above_serial_id (void *cls,
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_refunds_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
- TALER_EXCHANGEDB_RefundCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_refreshs_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+
TALER_EXCHANGEDB_RefreshSessionCallback cb,
+ void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- int nrows;
- int ret;
+ struct RefreshsSerialContext rsc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "audit_get_refresh_sessions_incr",
+ params,
+ &refreshs_serial_helper_cb,
+ &rsc);
+ if (GNUNET_OK != rsc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- result = GNUNET_PQ_exec_prepared (session->conn,
- "audit_get_refunds_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_refunds_above_serial_id() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+/**
+ * Closure for #refunds_serial_helper_cb().
+ */
+struct RefundsSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_RefundCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct RefundsSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+refunds_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct RefundsSerialContext *rsc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
struct TALER_EXCHANGEDB_Refund refund;
struct TALER_DenominationPublicKey denom_pub;
uint64_t rowid;
-
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
&refund.merchant_pub),
@@ -5697,80 +5244,112 @@ postgres_select_refunds_above_serial_id (void *cls,
&rowid),
GNUNET_PQ_result_spec_end
};
+ int ret;
+
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ rsc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- &denom_pub,
- &refund.coin.coin_pub,
- &refund.merchant_pub,
- &refund.merchant_sig,
- &refund.h_contract_terms,
- refund.rtransaction_id,
- &refund.refund_amount);
+ ret = rsc->cb (rsc->cb_cls,
+ rowid,
+ &denom_pub,
+ &refund.coin.coin_pub,
+ &refund.merchant_pub,
+ &refund.merchant_sig,
+ &refund.h_contract_terms,
+ refund.rtransaction_id,
+ &refund.refund_amount);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Select inbound wire transfers into reserves_in above @a serial_id
- * in monotonically increasing order.
+ * Select refunds above @a serial_id in monotonically increasing
+ * order.
*
* @param cls closure
* @param session database connection
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_reserves_in_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
-
TALER_EXCHANGEDB_ReserveInCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_refunds_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+ TALER_EXCHANGEDB_RefundCallback cb,
+ void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "audit_reserves_in_get_transactions_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- int nrows;
- int ret;
+ struct RefundsSerialContext rsc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "audit_get_refunds_incr",
+ params,
+ &refunds_serial_helper_cb,
+ &rsc);
+ if (GNUNET_OK != rsc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_reserves_in_above_serial_id() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+/**
+ * Closure for #reserves_in_serial_helper_cb().
+ */
+struct ReservesInSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_ReserveInCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct ReservesInSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserves_in_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReservesInSerialContext *risc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount credit;
@@ -5779,7 +5358,6 @@ postgres_select_reserves_in_above_serial_id (void *cls,
uint64_t rowid;
void *wire_reference;
size_t wire_reference_size;
-
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
@@ -5796,6 +5374,7 @@ postgres_select_reserves_in_above_serial_id (void *cls,
&rowid),
GNUNET_PQ_result_spec_end
};
+ int ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
@@ -5803,29 +5382,26 @@ postgres_select_reserves_in_above_serial_id (void *cls,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ risc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- &reserve_pub,
- &credit,
- sender_account_details,
- wire_reference,
- wire_reference_size,
- execution_date);
+ ret = risc->cb (risc->cb_cls,
+ rowid,
+ &reserve_pub,
+ &credit,
+ sender_account_details,
+ wire_reference,
+ wire_reference_size,
+ execution_date);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
-
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Select withdraw operations from reserves_out above @a serial_id
+ * Select inbound wire transfers into reserves_in above @a serial_id
* in monotonically increasing order.
*
* @param cls closure
@@ -5833,44 +5409,76 @@ postgres_select_reserves_in_above_serial_id (void *cls,
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if no records were found
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_reserves_out_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
-
TALER_EXCHANGEDB_WithdrawCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_reserves_in_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+
TALER_EXCHANGEDB_ReserveInCallback cb,
+ void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "audit_get_reserves_out_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- int nrows;
- int ret;
+ struct ReservesInSerialContext risc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+
"audit_reserves_in_get_transactions_incr",
+ params,
+ &reserves_in_serial_helper_cb,
+ &risc);
+ if (GNUNET_OK != risc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_reserves_out_above_serial_id() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #reserves_out_serial_helper_cb().
+ */
+struct ReservesOutSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_WithdrawCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct ReservesOutSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserves_out_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReservesOutSerialContext *rosc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
struct GNUNET_HashCode h_blind_ev;
struct TALER_DenominationPublicKey denom_pub;
@@ -5880,7 +5488,6 @@ postgres_select_reserves_out_above_serial_id (void *cls,
struct GNUNET_TIME_Absolute execution_date;
struct TALER_Amount amount_with_fee;
uint64_t rowid;
-
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
&h_blind_ev),
@@ -5900,89 +5507,118 @@ postgres_select_reserves_out_above_serial_id (void
*cls,
&rowid),
GNUNET_PQ_result_spec_end
};
+ int ret;
+
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ rosc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- &h_blind_ev,
- &denom_pub,
- &denom_sig,
- &reserve_pub,
- &reserve_sig,
- execution_date,
- &amount_with_fee);
+ ret = rosc->cb (rosc->cb_cls,
+ rowid,
+ &h_blind_ev,
+ &denom_pub,
+ &denom_sig,
+ &reserve_pub,
+ &reserve_sig,
+ execution_date,
+ &amount_with_fee);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
-
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Function called to select all wire transfers the exchange
- * executed.
+ * Select withdraw operations from reserves_out above @a serial_id
+ * in monotonically increasing order.
*
* @param cls closure
* @param session database connection
* @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call for ONE unfinished item
+ * @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_wire_out_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
-
TALER_EXCHANGEDB_WireTransferOutCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_reserves_out_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+
TALER_EXCHANGEDB_WithdrawCallback cb,
+ void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- int nrows;
- int ret;
+ struct ReservesOutSerialContext rosc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "audit_get_reserves_out_incr",
+ params,
+ &reserves_out_serial_helper_cb,
+ &rosc);
+ if (GNUNET_OK != rosc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- result = GNUNET_PQ_exec_prepared (session->conn,
- "audit_get_wire_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_prepare_above_serial_id() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+/**
+ * Closure for #wire_out_serial_helper_cb().
+ */
+struct WireOutSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_WireTransferOutCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct WireOutSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+wire_out_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct WireOutSerialContext *wosc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
uint64_t rowid;
struct GNUNET_TIME_Absolute date;
struct TALER_WireTransferIdentifierRawP wtid;
json_t *wire;
struct TALER_Amount amount;
-
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
&rowid),
@@ -5996,6 +5632,7 @@ postgres_select_wire_out_above_serial_id (void *cls,
&amount),
GNUNET_PQ_result_spec_end
};
+ int ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
@@ -6003,73 +5640,101 @@ postgres_select_wire_out_above_serial_id (void *cls,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ wosc->status = GNUNET_SYSERR;
+ return;
}
-
- ret = cb (cb_cls,
- rowid,
- date,
- &wtid,
- wire,
- &amount);
+ ret = wosc->cb (wosc->cb_cls,
+ rowid,
+ date,
+ &wtid,
+ wire,
+ &amount);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
-
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Function called to select payback requests the exchange
- * received, ordered by serial ID (monotonically increasing).
+ * Function called to select all wire transfers the exchange
+ * executed.
*
* @param cls closure
* @param session database connection
- * @param serial_id lowest serial ID to include (select larger or equal)
+ * @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_payback_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
- TALER_EXCHANGEDB_PaybackCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_wire_out_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+
TALER_EXCHANGEDB_WireTransferOutCallback cb,
+ void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "payback_get_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- int nrows;
- int ret;
+ struct WireOutSerialContext wosc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "audit_get_wire_incr",
+ params,
+ &wire_out_serial_helper_cb,
+ &wosc);
+ if (GNUNET_OK != wosc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_prepare_above_serial_id() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #payback_serial_helper_cb().
+ */
+struct PaybackSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_PaybackCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct PaybackSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+payback_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct PaybackSerialContext *psc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
uint64_t rowid;
struct TALER_ReservePublicKeyP reserve_pub;
@@ -6102,6 +5767,7 @@ postgres_select_payback_above_serial_id (void *cls,
&amount),
GNUNET_PQ_result_spec_end
};
+ int ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
@@ -6109,74 +5775,103 @@ postgres_select_payback_above_serial_id (void *cls,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ psc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- timestamp,
- &amount,
- &reserve_pub,
- &coin,
- &coin_sig,
- &coin_blind);
+ ret = psc->cb (psc->cb_cls,
+ rowid,
+ timestamp,
+ &amount,
+ &reserve_pub,
+ &coin,
+ &coin_sig,
+ &coin_blind);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
-
- PQclear (result);
- return GNUNET_OK;
}
/**
- * Function called to select reserve close operations the aggregator
- * triggered, ordered by serial ID (monotonically increasing).
+ * Function called to select payback requests the exchange
+ * received, ordered by serial ID (monotonically increasing).
*
* @param cls closure
* @param session database connection
* @param serial_id lowest serial ID to include (select larger or equal)
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
-postgres_select_reserve_closed_above_serial_id (void *cls,
- struct TALER_EXCHANGEDB_Session
*session,
- uint64_t serial_id,
-
TALER_EXCHANGEDB_ReserveClosedCallback cb,
- void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_payback_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+ TALER_EXCHANGEDB_PaybackCallback cb,
+ void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
GNUNET_PQ_query_param_end
};
- PGresult *result;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserves_close_get_incr",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- int nrows;
- int ret;
+ struct PaybackSerialContext psc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "payback_get_incr",
+ params,
+ &payback_serial_helper_cb,
+ &psc);
+ if (GNUNET_OK != psc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "select_reserve_closed_above_serial_id() returned 0 matching
rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #reserve_closed_serial_helper_cb().
+ */
+struct ReserveClosedSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_ReserveClosedCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct ReserveClosedSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserve_closed_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveClosedSerialContext *rcsc = cls;
+
+ for (unsigned int i=0;i<num_results;i++)
{
uint64_t rowid;
struct TALER_ReservePublicKeyP reserve_pub;
@@ -6202,6 +5897,7 @@ postgres_select_reserve_closed_above_serial_id (void *cls,
&closing_fee),
GNUNET_PQ_result_spec_end
};
+ int ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
@@ -6209,24 +5905,61 @@ postgres_select_reserve_closed_above_serial_id (void
*cls,
i))
{
GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
+ rcsc->status = GNUNET_SYSERR;
+ return;
}
- ret = cb (cb_cls,
- rowid,
- execution_date,
- &amount_with_fee,
- &closing_fee,
- &reserve_pub,
- receiver_account,
- &wtid);
+ ret = rcsc->cb (rcsc->cb_cls,
+ rowid,
+ execution_date,
+ &amount_with_fee,
+ &closing_fee,
+ &reserve_pub,
+ receiver_account,
+ &wtid);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
+}
- PQclear (result);
- return GNUNET_OK;
+
+/**
+ * Function called to select reserve close operations the aggregator
+ * triggered, ordered by serial ID (monotonically increasing).
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param serial_id lowest serial ID to include (select larger or equal)
+ * @param cb function to call for ONE unfinished item
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_reserve_closed_above_serial_id (void *cls,
+ struct TALER_EXCHANGEDB_Session
*session,
+ uint64_t serial_id,
+
TALER_EXCHANGEDB_ReserveClosedCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&serial_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct ReserveClosedSerialContext rcsc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "reserves_close_get_incr",
+ params,
+ &reserve_closed_serial_helper_cb,
+ &rcsc);
+ if (GNUNET_OK != rcsc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
}
@@ -6245,11 +5978,9 @@ postgres_select_reserve_closed_above_serial_id (void
*cls,
* @param amount total amount to be paid back
* @param h_blind_ev hash of the blinded coin's envelope (must match
reserves_out entry)
* @param timestamp current time (rounded)
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on DB errors
+ * @return transaction result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_payback_request (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP
*reserve_pub,
@@ -6273,49 +6004,32 @@ postgres_insert_payback_request (void *cls,
GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
GNUNET_PQ_query_param_end
};
- int ret;
+ enum GNUNET_DB_QueryStatus qs;
/* check if the coin is already known */
- ret = get_known_coin (cls,
- session,
- &coin->coin_pub,
- NULL);
- if (GNUNET_SYSERR == ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_NO == ret) /* if not, insert it */
- {
- if (GNUNET_OK !=
- (ret = insert_known_coin (cls,
- session,
- coin)))
- {
- GNUNET_break (GNUNET_NO == ret);
- return ret;
- }
- }
-
+ if (0 > (qs = ensure_coin_known (cls,
+ session,
+ coin)))
+ return qs;
/* now store actual payback information */
- if (GNUNET_OK !=
- (ret = execute_prepared_non_select (session,
- "payback_insert",
- params)))
+ qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "payback_insert",
+ params);
+ if (0 > qs)
{
- GNUNET_break (GNUNET_NO == ret);
- return ret;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
/* Update reserve balance */
reserve.pub = *reserve_pub;
- if (GNUNET_OK != postgres_reserve_get (cls,
- session,
- &reserve))
+ qs = postgres_reserve_get (cls,
+ session,
+ &reserve);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- /* Should have been checked before we got here... */
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
if (GNUNET_SYSERR ==
TALER_amount_add (&reserve.balance,
@@ -6323,21 +6037,21 @@ postgres_insert_payback_request (void *cls,
amount))
{
GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
expiry = GNUNET_TIME_absolute_add (timestamp,
pg->idle_reserve_expiration_time);
reserve.expiry = GNUNET_TIME_absolute_max (expiry,
reserve.expiry);
- ret = reserves_update (cls,
+ qs = reserves_update (cls,
session,
&reserve);
- if (GNUNET_SYSERR == ret)
+ if (0 >= qs)
{
- GNUNET_break (0);
- return GNUNET_SYSERR;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
- return ret;
+ return qs;
}
@@ -6349,11 +6063,9 @@ postgres_insert_payback_request (void *cls,
* @param session a session
* @param h_blind_ev hash of the blinded coin
* @param[out] reserve_pub set to information about the reserve (on success
only)
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_reserve_by_h_blind (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_blind_ev,
@@ -6363,47 +6075,16 @@ postgres_get_reserve_by_h_blind (void *cls,
GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "reserve_by_h_blind",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- int nrows;
-
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "reserve_by_h_blind() returned 0 matching rows\n");
- PQclear (result);
- return GNUNET_NO;
- }
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- reserve_pub),
- GNUNET_PQ_result_spec_end
- };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ reserve_pub),
+ GNUNET_PQ_result_spec_end
+ };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- }
- PQclear (result);
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "reserve_by_h_blind",
+ params,
+ rs);
}
@@ -6415,54 +6096,23 @@ postgres_get_reserve_by_h_blind (void *cls,
* @param session a session
* @param denom_pub_hash hash of the revoked denomination key
* @param master_sig signature affirming the revocation
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if the entry already exists (transaction must be rolled
back!)
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_denomination_revocation (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
const struct GNUNET_HashCode
*denom_pub_hash,
const struct TALER_MasterSignatureP
*master_sig)
{
- PGresult *result;
- int ret;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_end
};
- result = GNUNET_PQ_exec_prepared (session->conn,
- "denomination_revocation_insert",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- const char *efield;
-
- efield = PQresultErrorField (result,
- PG_DIAG_SQLSTATE);
- /* FIXME: what about serialization errors? */
- if ( (PGRES_FATAL_ERROR == PQresultStatus(result)) &&
- (NULL != strstr (PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION,
- efield)) )
- {
- /* This means we had the same reserve/justification/details
- before */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Uniqueness violation, revocation details already known\n");
- PQclear (result);
- return GNUNET_NO;
- }
- ret = GNUNET_SYSERR;
- BREAK_DB_ERR (result, session->conn);
- }
- else
- {
- ret = GNUNET_OK;
- }
- PQclear (result);
- return ret;
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "denomination_revocation_insert",
+ params);
}
@@ -6475,11 +6125,9 @@ postgres_insert_denomination_revocation (void *cls,
* @param denom_pub_hash hash of the revoked denomination key
* @param[out] master_sig signature affirming the revocation
* @param[out] rowid row where the information is stored
- * @return #GNUNET_OK on success,
- * #GNUNET_NO no such entry exists
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_get_denomination_revocation (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode
*denom_pub_hash,
@@ -6495,42 +6143,11 @@ postgres_get_denomination_revocation (void *cls,
GNUNET_PQ_result_spec_uint64 ("denom_revocations_serial_id", rowid),
GNUNET_PQ_result_spec_end
};
- PGresult *result;
- int nrows;
- result = GNUNET_PQ_exec_prepared (session->conn,
- "denomination_revocation_get",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- nrows = PQntuples (result);
- if (0 == nrows)
- {
- /* no matches found */
- PQclear (result);
- return GNUNET_NO;
- }
- if (1 != nrows)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
+ return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+
"denomination_revocation_get",
+ params,
+ rs);
}
@@ -6650,7 +6267,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->select_reserves_out_above_serial_id =
&postgres_select_reserves_out_above_serial_id;
plugin->select_wire_out_above_serial_id =
&postgres_select_wire_out_above_serial_id;
plugin->select_payback_above_serial_id =
&postgres_select_payback_above_serial_id;
- plugin->select_reserve_closed_above_serial_id =
&postgres_select_reserve_closed_above_serial_id;
+ plugin->select_reserve_closed_above_serial_id =
&postgres_select_reserve_closed_above_serial_id;
plugin->insert_payback_request = &postgres_insert_payback_request;
plugin->get_reserve_by_h_blind = &postgres_get_reserve_by_h_blind;
plugin->insert_denomination_revocation =
&postgres_insert_denomination_revocation;
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 232b58c..6b89577 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -98,7 +98,7 @@ mark_prepare_cb (void *cls,
GNUNET_assert (0 == memcmp (buf,
"hello world",
buf_size));
- GNUNET_break (GNUNET_OK ==
+ GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
plugin->wire_prepare_data_mark_finished (plugin->cls,
session,
rowid));
@@ -114,23 +114,23 @@ mark_prepare_cb (void *cls,
static int
test_wire_prepare (struct TALER_EXCHANGEDB_Session *session)
{
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->wire_prepare_data_get (plugin->cls,
session,
&dead_prepare_cb,
NULL));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->wire_prepare_data_insert (plugin->cls,
session,
"testcase",
"hello world",
11));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->wire_prepare_data_get (plugin->cls,
session,
&mark_prepare_cb,
session));
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->wire_prepare_data_get (plugin->cls,
session,
&dead_prepare_cb,
@@ -162,8 +162,7 @@ check_reserve (struct TALER_EXCHANGEDB_Session *session,
struct TALER_EXCHANGEDB_Reserve reserve;
reserve.pub = *pub;
-
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->reserve_get (plugin->cls,
session,
&reserve));
@@ -259,7 +258,7 @@ create_denom_key_pair (unsigned int size,
dki.issue.properties.purpose.size = htonl (sizeof (struct
TALER_DenominationKeyValidityPS));
dki.issue.properties.purpose.purpose = htonl
(TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
- if (GNUNET_OK !=
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_denomination_info (plugin->cls,
session,
&dki.denom_pub,
@@ -269,7 +268,7 @@ create_denom_key_pair (unsigned int size,
destroy_denom_key_pair (dkp);
return NULL;
}
- if (GNUNET_OK !=
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_denomination_info (plugin->cls,
session,
&dki.denom_pub,
@@ -349,7 +348,7 @@ test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session
*session,
ccoin->coin_ev,
ccoin->coin_ev_size);
}
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refresh_commit_coins (plugin->cls,
session,
session_hash,
@@ -357,7 +356,7 @@ test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session
*session,
commit_coins));
ret_commit_coins = GNUNET_new_array (MELT_NEW_COINS,
struct
TALER_EXCHANGEDB_RefreshCommitCoin);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_refresh_commit_coins (plugin->cls,
session,
session_hash,
@@ -410,19 +409,19 @@ test_refresh_commit_links (struct
TALER_EXCHANGEDB_Session *session,
unsigned int i;
ret = GNUNET_SYSERR;
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_refresh_transfer_public_key (plugin->cls,
session,
session_hash,
&tp));
for (i=0;i<TALER_CNC_KAPPA;i++)
RND_BLK (&rctp[i]);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refresh_transfer_public_key (plugin->cls,
session,
session_hash,
&rctp[MELT_NOREVEAL_INDEX]));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_refresh_transfer_public_key (plugin->cls,
session,
session_hash,
@@ -530,6 +529,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
struct TALER_DenominationSignature ev_sigs[MELT_NEW_COINS];
unsigned int cnt;
int ret;
+ enum GNUNET_DB_QueryStatus qs;
ret = GNUNET_SYSERR;
memset (ev_sigs, 0, sizeof (ev_sigs));
@@ -571,21 +571,24 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
meltp->melt_fee = fee_refresh;
}
- FAILIF (GNUNET_OK != plugin->create_refresh_session (plugin->cls,
- session,
- &session_hash,
- &refresh_session));
- FAILIF (GNUNET_OK != plugin->get_refresh_session (plugin->cls,
- session,
- &session_hash,
- &ret_refresh_session));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->create_refresh_session (plugin->cls,
+ session,
+ &session_hash,
+ &refresh_session));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->get_refresh_session (plugin->cls,
+ session,
+ &session_hash,
+ &ret_refresh_session));
auditor_row_cnt = 0;
- FAILIF (GNUNET_OK != plugin->select_refreshs_above_serial_id (plugin->cls,
- session,
- 0,
-
&audit_refresh_session_cb,
- NULL));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->select_refreshs_above_serial_id (plugin->cls,
+ session,
+ 0,
+ &audit_refresh_session_cb,
+ NULL));
FAILIF (1 != auditor_row_cnt);
FAILIF (ret_refresh_session.num_newcoins != refresh_session.num_newcoins);
FAILIF (ret_refresh_session.noreveal_index !=
refresh_session.noreveal_index);
@@ -632,7 +635,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
&fee_refund);
new_denom_pubs[cnt] = new_dkp[cnt]->pub;
}
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refresh_order (plugin->cls,
session,
&session_hash,
@@ -640,7 +643,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
new_denom_pubs));
ret_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
struct TALER_DenominationPublicKey);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_refresh_order (plugin->cls,
session,
&session_hash,
@@ -671,19 +674,19 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
= GNUNET_CRYPTO_rsa_sign_fdh (new_dkp[cnt]->priv.rsa_private_key,
&hc);
GNUNET_assert (NULL != ev_sigs[cnt].rsa_signature);
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_refresh_out (plugin->cls,
session,
&session_hash,
cnt,
&test_sig));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refresh_out (plugin->cls,
session,
&session_hash,
cnt,
&ev_sigs[cnt]));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_refresh_out (plugin->cls,
session,
&session_hash,
@@ -695,9 +698,11 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
GNUNET_CRYPTO_rsa_signature_free (test_sig.rsa_signature);
}
- ldl = plugin->get_link_data_list (plugin->cls,
- session,
- &session_hash);
+ qs = plugin->get_link_data_list (plugin->cls,
+ session,
+ &session_hash,
+ &ldl);
+ FAILIF (0 >= qs);
FAILIF (NULL == ldl);
for (ldlp = ldl; NULL != ldlp; ldlp = ldlp->next)
{
@@ -726,10 +731,13 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
{
/* Just to test fetching a coin with melt history */
struct TALER_EXCHANGEDB_TransactionList *tl;
+ enum GNUNET_DB_QueryStatus qs;
- tl = plugin->get_coin_transactions (plugin->cls,
+ qs = plugin->get_coin_transactions (plugin->cls,
session,
- &meltp->coin.coin_pub);
+ &meltp->coin.coin_pub,
+ &tl);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
plugin->free_coin_transaction_list (plugin->cls,
tl);
}
@@ -739,7 +747,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
int ok;
ok = GNUNET_NO;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_transfer (plugin->cls,
session,
&meltp->coin.coin_pub,
@@ -906,10 +914,9 @@ static uint64_t deposit_rowid;
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant, NULL from
iterate_matching_deposits()
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR if deposit does
- * not match our expectations
+ * @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to
continue to iterate
*/
-static int
+static enum GNUNET_DB_QueryStatus
deposit_cb (void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -945,10 +952,10 @@ deposit_cb (void *cls,
sizeof (struct GNUNET_HashCode))) ) )
{
GNUNET_break (0);
- return GNUNET_SYSERR;
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
- return GNUNET_OK;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -1115,7 +1122,7 @@ test_gc (struct TALER_EXCHANGEDB_Session *session)
destroy_denom_key_pair (dkp);
return GNUNET_SYSERR;
}
- if (GNUNET_OK ==
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_denomination_info (plugin->cls,
session,
&dkp->pub,
@@ -1156,7 +1163,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&master_sig,
sizeof (master_sig));
- if (GNUNET_OK !=
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_wire_fee (plugin->cls,
session,
"wire-method",
@@ -1168,7 +1175,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
GNUNET_break (0);
return GNUNET_SYSERR;
}
- if (GNUNET_OK !=
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->insert_wire_fee (plugin->cls,
session,
"wire-method",
@@ -1182,7 +1189,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
}
/* This must fail as 'end_date' is NOT in the
half-open interval [start_date,end_date) */
- if (GNUNET_OK ==
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_wire_fee (plugin->cls,
session,
"wire-method",
@@ -1195,7 +1202,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
GNUNET_break (0);
return GNUNET_SYSERR;
}
- if (GNUNET_OK !=
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_wire_fee (plugin->cls,
session,
"wire-method",
@@ -1299,7 +1306,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
TALER_amount_subtract (&transfer_value_wt,
&coin_value_wt,
&coin_fee_wt));
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->lookup_wire_transfer (plugin->cls,
session,
&wire_out_wtid,
@@ -1310,7 +1317,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
struct GNUNET_HashCode h_contract_terms_wt2 = h_contract_terms_wt;
h_contract_terms_wt2.bits[0]++;
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->wire_lookup_deposit_wtid (plugin->cls,
session,
&h_contract_terms_wt2,
@@ -1321,7 +1328,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
NULL));
}
/* insert WT data */
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_aggregation_tracking (plugin->cls,
session,
&wire_out_wtid,
@@ -1329,7 +1336,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
/* Now let's fix the transient constraint violation by
putting in the WTID into the wire_out table */
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->store_wire_transfer_out (plugin->cls,
session,
wire_out_date,
@@ -1337,17 +1344,17 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
wire_out_account,
&wire_out_amount));
/* And now the commit should still succeed! */
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls,
session));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->lookup_wire_transfer (plugin->cls,
session,
&wire_out_wtid,
&cb_wt_check,
&cb_wt_never));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->wire_lookup_deposit_wtid (plugin->cls,
session,
&h_contract_terms_wt,
@@ -1356,7 +1363,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
&merchant_pub_wt,
&cb_wtid_check,
&cb_wtid_never));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_wire_out_above_serial_id (plugin->cls,
session,
0,
@@ -1447,6 +1454,7 @@ run (void *cls)
unsigned int cnt;
void *rr;
size_t rr_size;
+ enum GNUNET_DB_QueryStatus qs;
dkp = NULL;
rh = NULL;
@@ -1475,8 +1483,12 @@ run (void *cls)
goto drop;
}
+ FAILIF (GNUNET_OK !=
+ plugin->start (plugin->cls,
+ session));
+
/* test DB is empty */
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->select_payback_above_serial_id (plugin->cls,
session,
0,
@@ -1505,12 +1517,12 @@ run (void *cls)
result = 4;
sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL);
GNUNET_assert (NULL != sndr);
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
&rr,
&rr_size));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->reserves_in_insert (plugin->cls,
session,
&reserve_pub,
@@ -1519,7 +1531,7 @@ run (void *cls)
sndr,
"TEST",
4));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
&rr,
@@ -1533,7 +1545,7 @@ run (void *cls)
value.value,
value.fraction,
value.currency));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->reserves_in_insert (plugin->cls,
session,
&reserve_pub,
@@ -1542,12 +1554,12 @@ run (void *cls)
sndr,
"TEST2",
5));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
&rr,
&rr_size));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls,
session,
&rr,
@@ -1583,7 +1595,7 @@ run (void *cls)
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_withdraw_info (plugin->cls,
session,
&cbc));
@@ -1594,7 +1606,7 @@ run (void *cls)
value.fraction,
value.currency));
- FAILIF (GNUNET_YES !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_reserve_by_h_blind (plugin->cls,
session,
&cbc.h_coin_envelope,
@@ -1603,7 +1615,7 @@ run (void *cls)
&reserve_pub2,
sizeof (reserve_pub)));
- FAILIF (GNUNET_YES !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_withdraw_info (plugin->cls,
session,
&cbc.h_coin_envelope,
@@ -1628,7 +1640,7 @@ run (void *cls)
deposit.coin.denom_pub = dkp->pub;
deposit.coin.denom_sig = cbc.sig;
deadline = GNUNET_TIME_absolute_get ();
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_payback_request (plugin->cls,
session,
&reserve_pub,
@@ -1638,7 +1650,7 @@ run (void *cls)
&value,
&cbc.h_coin_envelope,
deadline));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_payback_above_serial_id (plugin->cls,
session,
0,
@@ -1653,7 +1665,7 @@ run (void *cls)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.000010",
&fee_closing));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_reserve_closed (plugin->cls,
session,
&reserve_pub,
@@ -1671,9 +1683,11 @@ run (void *cls)
json_decref (sndr);
result = 7;
- rh = plugin->get_reserve_history (plugin->cls,
+ qs = plugin->get_reserve_history (plugin->cls,
session,
- &reserve_pub);
+ &reserve_pub,
+ &rh);
+ FAILIF (0 > qs);
FAILIF (NULL == rh);
rh_head = rh;
for (cnt=0; NULL != rh_head; rh_head=rh_head->next, cnt++)
@@ -1739,13 +1753,13 @@ run (void *cls)
FAILIF (5 != cnt);
auditor_row_cnt = 0;
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >=
plugin->select_reserves_in_above_serial_id (plugin->cls,
session,
0,
&audit_reserve_in_cb,
NULL));
- FAILIF (GNUNET_OK !=
+ FAILIF (0 >=
plugin->select_reserves_out_above_serial_id (plugin->cls,
session,
0,
@@ -1768,16 +1782,16 @@ run (void *cls)
deposit.amount_with_fee = value;
deposit.deposit_fee = fee_deposit;
result = 8;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_deposit (plugin->cls,
session,
&deposit));
- FAILIF (GNUNET_YES !=
+ FAILIF (1 !=
plugin->have_deposit (plugin->cls,
session,
&deposit));
auditor_row_cnt = 0;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_deposits_above_serial_id (plugin->cls,
session,
0,
@@ -1785,7 +1799,7 @@ run (void *cls)
NULL));
FAILIF (1 != auditor_row_cnt);
result = 9;
- FAILIF (1 !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->iterate_matching_deposits (plugin->cls,
session,
&deposit.h_wire,
@@ -1793,26 +1807,29 @@ run (void *cls)
&deposit_cb, &deposit,
2));
- FAILIF (1 !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_ready_deposit (plugin->cls,
session,
&deposit_cb,
&deposit));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->commit (plugin->cls,
+ session));
FAILIF (GNUNET_OK !=
plugin->start (plugin->cls,
session));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->mark_deposit_tiny (plugin->cls,
- session,
+ session,
deposit_rowid));
- FAILIF (0 !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_ready_deposit (plugin->cls,
session,
&deposit_cb,
&deposit));
plugin->rollback (plugin->cls,
session);
- FAILIF (1 !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_ready_deposit (plugin->cls,
session,
&deposit_cb,
@@ -1820,18 +1837,18 @@ run (void *cls)
FAILIF (GNUNET_OK !=
plugin->start (plugin->cls,
session));
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->test_deposit_done (plugin->cls,
session,
&deposit));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->mark_deposit_done (plugin->cls,
session,
deposit_rowid));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls,
session));
- FAILIF (GNUNET_YES !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->test_deposit_done (plugin->cls,
session,
&deposit));
@@ -1839,17 +1856,18 @@ run (void *cls)
result = 10;
deposit2 = deposit;
RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
- FAILIF (GNUNET_OK != test_melting (session));
+ FAILIF (GNUNET_OK !=
+ test_melting (session));
/* test insert_refund! */
@@ -1860,7 +1878,7 @@ run (void *cls)
refund.rtransaction_id = GNUNET_CRYPTO_random_u64
(GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
refund.refund_amount = deposit.amount_with_fee;
refund.refund_fee = fee_refund;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refund (plugin->cls,
session,
&refund));
@@ -1868,18 +1886,18 @@ run (void *cls)
/* test payback / revocation */
RND_BLK (&master_sig);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_denomination_revocation (plugin->cls,
session,
&dkp_pub_hash,
&master_sig));
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->commit (plugin->cls,
session));
FAILIF (GNUNET_OK !=
plugin->start (plugin->cls,
session));
- FAILIF (GNUNET_NO !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->insert_denomination_revocation (plugin->cls,
session,
&dkp_pub_hash,
@@ -1893,7 +1911,7 @@ run (void *cls)
struct TALER_MasterSignatureP msig;
uint64_t rev_rowid;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_denomination_revocation (plugin->cls,
session,
&dkp_pub_hash,
@@ -1907,7 +1925,7 @@ run (void *cls)
RND_BLK (&coin_sig);
RND_BLK (&coin_blind);
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_payback_request (plugin->cls,
session,
&reserve_pub,
@@ -1919,7 +1937,7 @@ run (void *cls)
deadline));
auditor_row_cnt = 0;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_refunds_above_serial_id (plugin->cls,
session,
0,
@@ -1927,9 +1945,11 @@ run (void *cls)
NULL));
FAILIF (1 != auditor_row_cnt);
- tl = plugin->get_coin_transactions (plugin->cls,
+ qs = plugin->get_coin_transactions (plugin->cls,
session,
- &refund.coin.coin_pub);
+ &refund.coin.coin_pub,
+ &tl);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
GNUNET_assert (NULL != tl);
matched = 0;
for (tlp = tl; NULL != tlp; tlp = tlp->next)
@@ -2111,3 +2131,5 @@ main (int argc,
GNUNET_free (testname);
return result;
}
+
+/* end of test_exchangedb.c */
diff --git a/src/include/taler_auditordb_plugin.h
b/src/include/taler_auditordb_plugin.h
index 170a68f..3141ad6 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -24,6 +24,7 @@
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_db_lib.h>
#include "taler_auditordb_lib.h"
#include "taler_signatures.h"
@@ -236,10 +237,9 @@ struct TALER_AUDITORDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
- * @return #GNUNET_OK on success, #GNUNET_NO if the transaction
- * can be retried, #GNUNET_SYSERR on hard failures
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*commit) (void *cls,
struct TALER_AUDITORDB_Session *session);
@@ -275,9 +275,9 @@ struct TALER_AUDITORDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param issue issuing information with value, fees and other info about
the denomination
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return status of database operation
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_denomination_info)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_DenominationKeyValidityPS
*issue);
@@ -291,9 +291,9 @@ struct TALER_AUDITORDB_Plugin
* @param master_pub master public key of the exchange
* @param cb function to call with the results
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_denomination_info)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -309,9 +309,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master key of the exchange
* @param pp where is the auditor in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_auditor_progress)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -326,9 +326,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master key of the exchange
* @param pp where is the auditor in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_auditor_progress)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -342,10 +342,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master key of the exchange
* @param[out] pp set to where the auditor is in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
- * #GNUNET_NO if we have no records for the @a master_pub
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_auditor_progress)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -364,9 +363,9 @@ struct TALER_AUDITORDB_Plugin
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
* @param expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_reserve_info)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -388,9 +387,9 @@ struct TALER_AUDITORDB_Plugin
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
* @param expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_reserve_info)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -412,10 +411,9 @@ struct TALER_AUDITORDB_Plugin
* @param[out] withdraw_fee_balance amount the exchange gained in withdraw
fees
* due to withdrawals from this reserve
* @param[out] expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this reserve; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_reserve_info)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -433,10 +431,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param reserve_pub public key of the reserve
* @param master_pub master public key of the exchange
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this reserve; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*del_reserve_info)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -453,9 +450,9 @@ struct TALER_AUDITORDB_Plugin
* @param reserve_balance amount stored in the reserve
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_reserve_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -473,9 +470,9 @@ struct TALER_AUDITORDB_Plugin
* @param reserve_balance amount stored in the reserve
* @param withdraw_fee_balance amount the exchange gained in withdraw fees
* due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_reserve_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -492,10 +489,9 @@ struct TALER_AUDITORDB_Plugin
* @param[out] reserve_balance amount stored in the reserve
* @param[out] withdraw_fee_balance amount the exchange gained in withdraw
fees
* due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this exchange; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_reserve_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -511,9 +507,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master public key of the exchange
* @param wire_fee_balance amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_wire_fee_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -528,9 +524,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master public key of the exchange
* @param wire_fee_balance amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_wire_fee_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -544,10 +540,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master public key of the exchange
* @param[out] wire_fee_balance set amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- * record about this exchange; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_wire_fee_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -563,9 +558,9 @@ struct TALER_AUDITORDB_Plugin
* @param denom_pub_hash hash of the denomination public key
* @param denom_balance value of coins outstanding with this denomination key
* @param denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_denomination_balance)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode *denom_pub_hash,
@@ -582,9 +577,9 @@ struct TALER_AUDITORDB_Plugin
* @param denom_pub_hash hash of the denomination public key
* @param denom_balance value of coins outstanding with this denomination key
* @param denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_denomination_balance)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode *denom_pub_hash,
@@ -600,9 +595,9 @@ struct TALER_AUDITORDB_Plugin
* @param denom_pub_hash hash of the denomination public key
* @param[out] denom_balance value of coins outstanding with this
denomination key
* @param[out] denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_NO if no record found,
#GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_denomination_balance)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode *denom_pub_hash,
@@ -616,9 +611,9 @@ struct TALER_AUDITORDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param denom_pub_hash hash of the denomination public key
- * @return #GNUNET_OK on success; #GNUNET_NO if no record found,
#GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*del_denomination_balance)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct GNUNET_HashCode *denom_pub_hash);
@@ -636,9 +631,9 @@ struct TALER_AUDITORDB_Plugin
* @param melt_fee_balance total melt fees collected for this DK
* @param refund_fee_balance total refund fees collected for this DK
* @param risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_balance_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -661,9 +656,9 @@ struct TALER_AUDITORDB_Plugin
* @param melt_fee_balance total melt fees collected for this DK
* @param refund_fee_balance total refund fees collected for this DK
* @param risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_balance_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -685,10 +680,9 @@ struct TALER_AUDITORDB_Plugin
* @param[out] melt_fee_balance total melt fees collected for this DK
* @param[out] refund_fee_balance total refund fees collected for this DK
* @param[out] risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no entry
- * for this @a master_pub; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_balance_summary)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -711,9 +705,9 @@ struct TALER_AUDITORDB_Plugin
* @param revenue_balance what was the total profit made from
* deposit fees, melting fees, refresh fees
* and coins that were never returned?
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_historic_denom_revenue)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -731,9 +725,9 @@ struct TALER_AUDITORDB_Plugin
* @param master_pub master key of the exchange
* @param cb function to call with the results
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_historic_denom_revenue)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -754,9 +748,9 @@ struct TALER_AUDITORDB_Plugin
* @param denom_pub_hash hash of the denomination key
* @param loss_timestamp when did this profit get realized
* @param loss_balance what was the total loss
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_historic_losses)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -773,9 +767,9 @@ struct TALER_AUDITORDB_Plugin
* @param master_pub master key of the exchange
* @param cb function to call with the results
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_historic_losses)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -792,9 +786,9 @@ struct TALER_AUDITORDB_Plugin
* @param start_time beginning of aggregated time interval
* @param end_time end of aggregated time interval
* @param reserve_profits total profits made
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_historic_reserve_revenue)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -811,9 +805,9 @@ struct TALER_AUDITORDB_Plugin
* @param master_pub master key of the exchange
* @param cb function to call with results
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_historic_reserve_revenue)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP
*master_pub,
@@ -830,9 +824,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master key of the exchange
* @param balance what the bank account balance of the exchange should show
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_predicted_result)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -847,9 +841,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master key of the exchange
* @param balance what the bank account balance of the exchange should show
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*update_predicted_result)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
@@ -863,10 +857,9 @@ struct TALER_AUDITORDB_Plugin
* @param session connection to use
* @param master_pub master key of the exchange
* @param[out] balance expected bank account balance of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
- * #GNUNET_NO if we have no records for the @a master_pub
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_predicted_balance)(void *cls,
struct TALER_AUDITORDB_Session *session,
const struct TALER_MasterPublicKeyP *master_pub,
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 40a9b1a..907be84 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -137,6 +137,14 @@ enum TALER_ErrorCode
*/
TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1010,
+ /**
+ * Internal logic error. Some server-side function
+ * failed that really should not.
+ * This response is provided with HTTP status code
+ * MHD_HTTP_INTERNAL_SERVER_ERROR.
+ */
+ TALER_EC_INTERNAL_LOGIC_ERROR = 1011,
+
/* ********** request-specific error codes ************* */
@@ -441,14 +449,6 @@ enum TALER_ErrorCode
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR = 1304,
/**
- * The exchange failed to store refresh order data in the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR = 1305,
-
- /**
* The exchange failed to store commit data in the
* database.
* This response is provided with HTTP status code
@@ -457,14 +457,6 @@ enum TALER_ErrorCode
TALER_EC_REFRESH_MELT_DB_STORE_COMMIT_ERROR = 1306,
/**
- * The exchange failed to store transfer keys in the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR = 1307,
-
- /**
* The exchange is unaware of the denomination key that was
* requested for one of the fresh coins. This response is provided
* with HTTP status code MHD_HTTP_BAD_REQUEST.
@@ -903,7 +895,13 @@ enum TALER_ErrorCode
*/
TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE = 1857,
-
+ /**
+ * The "have" parameter was not a natural number.
+ * This reponse is provied with an HTTP status code of
+ * MHD_HTTP_BAD_REQUEST.
+ */
+ TALER_EC_KEYS_HAVE_NOT_NUMERIC = 1900,
+
/* *********** Merchant backend error codes ********* */
@@ -1282,6 +1280,36 @@ enum TALER_ErrorCode
*/
TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND = 2503,
+ /**
+ * The client specified an unknown instance for any of the /refund operations
+ */
+ TALER_EC_REFUND_INSTANCE_UNKNOWN = 2600,
+
+ /**
+ * The frontend gave an unknown order id to issue the refund to.
+ */
+ TALER_EC_REFUND_ORDER_ID_UNKNOWN = 2601,
+
+ /**
+ * The amount to be refunded is inconsistent: either is lower than
+ * the previous amount being awarded, or it is too big to be paid back.
+ * In this second case, the fault stays on the business dept. side.
+ */
+ TALER_EC_REFUND_INCONSISTENT_AMOUNT = 2602,
+
+ /**
+ * The backend encountered an error while trying to retrieve the
+ * payment data from database. Likely to be an internal error.
+ */
+ TALER_EC_REFUND_LOOKUP_DB_ERROR = 2603,
+
+ /**
+ * The backend encountered an error while trying to retrieve the
+ * payment data from database. Likely to be an internal error.
+ */
+ TALER_EC_REFUND_MERCHANT_DB_COMMIT_ERROR = 2604,
+
+
/* ********** /test API error codes ************* */
/**
diff --git a/src/include/taler_exchange_service.h
b/src/include/taler_exchange_service.h
index 9a004a4..487304c 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -206,6 +206,13 @@ struct TALER_EXCHANGE_Keys
char *version;
/**
+ * Timestamp indicating the /keys generation. Monotonically
+ * increasing. Used to fetch /keys incrementally. Set from
+ * the "list_issue_date" timestamp of /keys.
+ */
+ struct GNUNET_TIME_Absolute last_issue_date;
+
+ /**
* Length of the @e sign_keys array.
*/
unsigned int num_sign_keys;
@@ -223,6 +230,58 @@ struct TALER_EXCHANGE_Keys
};
+/**
+ * How compatible are the protocol version of the exchange and this
+ * client? The bits (1,2,4) can be used to test if the exchange's
+ * version is incompatible, older or newer respectively.
+ */
+enum TALER_EXCHANGE_VersionCompatibility
+{
+
+ /**
+ * The exchange runs exactly the same protocol version.
+ */
+ TALER_EXCHANGE_VC_MATCH = 0,
+
+ /**
+ * The exchange is too old or too new to be compatible with this
+ * implementation (bit)
+ */
+ TALER_EXCHANGE_VC_INCOMPATIBLE = 1,
+
+ /**
+ * The exchange is older than this implementation (bit)
+ */
+ TALER_EXCHANGE_VC_OLDER = 2,
+
+ /**
+ * The exchange is too old to be compatible with
+ * this implementation.
+ */
+ TALER_EXCHANGE_VC_INCOMPATIBLE_OUTDATED
+ = TALER_EXCHANGE_VC_INCOMPATIBLE
+ | TALER_EXCHANGE_VC_OLDER,
+
+ /**
+ * The exchange is more recent than this implementation (bit).
+ */
+ TALER_EXCHANGE_VC_NEWER = 4,
+
+ /**
+ * The exchange is too recent for this implementation.
+ */
+ TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER
+ = TALER_EXCHANGE_VC_INCOMPATIBLE
+ | TALER_EXCHANGE_VC_NEWER,
+
+ /**
+ * We could not even parse the version data.
+ */
+ TALER_EXCHANGE_VC_PROTOCOL_ERROR = 8
+
+};
+
+
/**
* Function called with information about who is auditing
* a particular exchange and what key the exchange is using.
@@ -230,10 +289,12 @@ struct TALER_EXCHANGE_Keys
* @param cls closure
* @param keys information about the various keys used
* by the exchange, NULL if /keys failed
+ * @param compat protocol compatibility information
*/
typedef void
(*TALER_EXCHANGE_CertificationCallback) (void *cls,
- const struct TALER_EXCHANGE_Keys
*keys);
+ const struct TALER_EXCHANGE_Keys
*keys,
+ enum
TALER_EXCHANGE_VersionCompatibility compat);
/**
diff --git a/src/include/taler_exchangedb_plugin.h
b/src/include/taler_exchangedb_plugin.h
index 5539d52..2cc2c75 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -24,6 +24,7 @@
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_db_lib.h>
#include "taler_exchangedb_lib.h"
@@ -673,9 +674,9 @@ struct TALER_EXCHANGEDB_Session;
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param receiver_wire_account wire details for the merchant, NULL from
iterate_matching_deposits()
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to
continue to iterate
*/
-typedef int
+typedef enum GNUNET_DB_QueryStatus
(*TALER_EXCHANGEDB_DepositIterator)(void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP
*merchant_pub,
@@ -1008,9 +1009,9 @@ typedef int
* @param left amount left in the reserve
* @param account_details information about the reserve's bank account
* @param expiration_date when did the reserve expire
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code to pass on
*/
-typedef int
+typedef enum GNUNET_DB_QueryStatus
(*TALER_EXCHANGEDB_ReserveExpiredCallback)(void *cls,
const struct TALER_ReservePublicKeyP
*reserve_pub,
const struct TALER_Amount *left,
@@ -1106,10 +1107,9 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
- * @return #GNUNET_OK on success, #GNUNET_NO if the transaction
- * can be retried, #GNUNET_SYSERR on hard failures
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*commit) (void *cls,
struct TALER_EXCHANGEDB_Session *session);
@@ -1134,9 +1134,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session connection to use
* @param denom_pub the public key used for signing coins of this
denomination
* @param issue issuing information with value, fees and other info about
the denomination
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return status of the query
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_denomination_info) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_DenominationPublicKey
*denom_pub,
@@ -1149,10 +1149,10 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param denom_pub the public key used for signing coins of this
denomination
- * @param[out] issue set to issue information with value, fees and other
info about the coin, can be NULL
- * @return #GNUNET_OK on success; #GNUNET_NO if no record was found,
#GNUNET_SYSERR on failure
+ * @param[out] issue set to issue information with value, fees and other
info about the coin
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_denomination_info) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_DenominationPublicKey
*denom_pub,
@@ -1167,9 +1167,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param[in,out] reserve the reserve data. The public key of the reserve
should be set
* in this structure; it is used to query the database. The balance
* and expiration are then filled accordingly.
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*reserve_get) (void *cls,
struct TALER_EXCHANGEDB_Session *db,
struct TALER_EXCHANGEDB_Reserve *reserve);
@@ -1189,11 +1189,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param sender_account_details information about the sender's bank account
* @param wire_reference unique reference identifying the wire transfer
(binary blob)
* @param wire_reference_size number of bytes in @a wire_reference
- * @return #GNUNET_OK upon success; #GNUNET_NO if the given
- * @a details are already known for this @a reserve_pub,
- * #GNUNET_SYSERR upon failures (DB error, incompatible currency)
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*reserves_in_insert) (void *cls,
struct TALER_EXCHANGEDB_Session *db,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1211,10 +1209,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param db the database connection handle
* @param[out] wire_reference set to unique reference identifying the wire
transfer (binary blob)
* @param[out] wire_reference_size set to number of bytes in @a
wire_reference
- * @return #GNUNET_OK upon success; #GNUNET_NO if we never got any incoming
transfers
- * #GNUNET_SYSERR upon failures (DB error)
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_latest_reserve_in_reference)(void *cls,
struct TALER_EXCHANGEDB_Session *db,
void **wire_reference,
@@ -1231,11 +1228,9 @@ struct TALER_EXCHANGEDB_Plugin
* `h_coin_envelope` in the @a collectable to be returned)
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
- * @return #GNUNET_SYSERR on internal error
- * #GNUNET_NO if the collectable was not found
- * #GNUNET_YES on success
+ * @return statement execution status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_withdraw_info) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_blind,
@@ -1250,11 +1245,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection to use
* @param collectable corresponding collectable coin (blind signature)
* if a coin is found
- * @return #GNUNET_SYSERR on internal error
- * #GNUNET_NO if the collectable was not found
- * #GNUNET_YES on success
+ * @return statement execution status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_withdraw_info) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_CollectableBlindcoin
*collectable);
@@ -1267,12 +1260,14 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to use
* @param reserve_pub public key of the reserve
- * @return known transaction history (NULL if reserve is unknown)
+ * @param[out] rhp set to known transaction history (NULL if reserve is
unknown)
+ * @return transaction status
*/
- struct TALER_EXCHANGEDB_ReserveHistory *
+ enum GNUNET_DB_QueryStatus
(*get_reserve_history) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_ReservePublicKeyP *reserve_pub);
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ struct TALER_EXCHANGEDB_ReserveHistory **rhp);
/**
@@ -1292,11 +1287,11 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param deposit deposit to search for
- * @return #GNUNET_YES if we know this operation,
- * #GNUNET_NO if this exact deposit is unknown to us,
- * #GNUNET_SYSERR on DB error
+ * @return 1 if we know this operation,
+ * 0 if this exact deposit is unknown to us,
+ * otherwise transaction error status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*have_deposit) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1308,11 +1303,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit deposit information to store
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_deposit) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1324,11 +1317,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param refund refund information to store
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_refund) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund);
@@ -1342,27 +1333,26 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit_rowid identifies the deposit row to modify
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*mark_deposit_tiny) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t rowid);
/**
- * Test if a deposit was marked as done, thereby declaring that it cannot be
- * refunded anymore.
+ * Test if a deposit was marked as done, thereby declaring that it
+ * cannot be refunded anymore.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit the deposit to check
- * @return #GNUNET_YES if is is marked done done, #GNUNET_NO if not,
- * #GNUNET_SYSERR on error (deposit unknown)
+ * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
+ * otherwise transaction error status (incl. deposit unknown)
*/
- int
+ enum GNUNET_DB_QueryStatus
(*test_deposit_done) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1376,11 +1366,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit_rowid identifies the deposit row to modify
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*mark_deposit_done) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t rowid);
@@ -1395,10 +1383,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session connection to the database
* @param deposit_cb function to call for ONE such deposit
* @param deposit_cb_cls closure for @a deposit_cb
- * @return number of rows processed, 0 if none exist,
- * #GNUNET_SYSERR on error
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_ready_deposit) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
TALER_EXCHANGEDB_DepositIterator deposit_cb,
@@ -1430,9 +1417,9 @@ struct TALER_EXCHANGEDB_Plugin
* be #TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT, larger values
* are not supported, smaller values would be inefficient.
* @return number of rows processed, 0 if none exist,
- * #GNUNET_SYSERR on error
+ * transaction status code on error
*/
- int
+ enum GNUNET_DB_QueryStatus
(*iterate_matching_deposits) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_wire,
@@ -1449,11 +1436,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database handle to use
* @param session_hash hash over the melt to use for the lookup
* @param[out] refresh_session where to store the result
- * @return #GNUNET_YES on success,
- * #GNUNET_NO if not found,
- * #GNUNET_SYSERR on DB failure
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_refresh_session) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1467,11 +1452,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database handle to use
* @param session_hash hash over the melt to use to locate the session
* @param refresh_session session data to store
- * @return #GNUNET_YES on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on DB failure
+ * @return query status for the transaction
*/
- int
+ enum GNUNET_DB_QueryStatus
(*create_refresh_session) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1487,11 +1470,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session
* @param num_newcoins number of coins to generate, size of the @a
denom_pubs array
* @param denom_pubs array denominations of the coins to create
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on internal error
+ * @return query status for the transaction
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_refresh_order) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1508,10 +1489,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session
* @param num_newcoins size of the @a denom_pubs array
* @param[out] denom_pubs where to write @a num_newcoins denomination keys
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_refresh_order) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1528,11 +1508,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session
* @param num_newcoins coin index size of the @a commit_coins array
* @param commit_coin array of coin commitments to store
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query status for the transaction
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_refresh_commit_coins) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1549,11 +1527,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session
* @param num_coins size of the @a commit_coins array
* @param[out] commit_coins array of coin commitments to return
- * @return #GNUNET_OK on success
- * #GNUNET_NO if not found
- * #GNUNET_SYSERR on error
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_refresh_commit_coins) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1581,11 +1557,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection to use
* @param session_hash hash to identify refresh session
* @param tp public key to store
- * @return #GNUNET_SYSERR on internal error
- * #GNUNET_NO on transient errors
- * #GNUNET_OK on success
+ * @return query status for the transaction
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_refresh_transfer_public_key) (void *cls,
struct TALER_EXCHANGEDB_Session
*session,
const struct GNUNET_HashCode
*session_hash,
@@ -1599,11 +1573,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection to use
* @param session_hash hash to identify refresh session
* @param[out] tp information to return
- * @return #GNUNET_SYSERR on internal error,
- * #GNUNET_NO if commitment was not found
- * #GNUNET_OK on success
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_refresh_transfer_public_key) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode
*session_hash,
@@ -1620,10 +1592,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session
* @param newcoin_index coin index
* @param[out] ev_sig coin signature
- * @return #GNUNET_OK on success, #GNUNET_NO if we have no such entry,
- * #GNUNET_SYSERR on error
+ * @return transaction result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_refresh_out) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1642,10 +1613,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session
* @param newcoin_index coin index
* @param ev_sig coin signature
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on error
+ * @return transaction result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_refresh_out) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash,
@@ -1660,12 +1630,14 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param session_hash session to get linkage data for
- * @return all known link data for the session
+ * @param[out] ldldp set to all known link data for the session
+ * @return status of the transaction
*/
- struct TALER_EXCHANGEDB_LinkDataList *
+ enum GNUNET_DB_QueryStatus
(*get_link_data_list) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash);
+ const struct GNUNET_HashCode *session_hash,
+ struct TALER_EXCHANGEDB_LinkDataList **ldlp);
/**
@@ -1690,11 +1662,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param coin_pub public key of the coin
* @param tdc function to call for each session the coin was melted into
* @param tdc_cls closure for @a tdc
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on failure (not found)
- * #GNUNET_SYSERR on internal failure (database issue)
+ * @return statement execution status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_transfer) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@@ -1709,12 +1679,14 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param coin_pub coin to investigate
- * @return list of transactions, NULL if coin is fresh
+ * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @return database transaction status
*/
- struct TALER_EXCHANGEDB_TransactionList *
+ enum GNUNET_DB_QueryStatus
(*get_coin_transactions) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_CoinSpendPublicKeyP *coin_pub);
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_EXCHANGEDB_TransactionList **tlp);
/**
@@ -1737,10 +1709,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
- * #GNUNET_NO if we found no results
+ * @return query status of the transaction
*/
- int
+ enum GNUNET_DB_QueryStatus
(*lookup_wire_transfer) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
@@ -1761,10 +1732,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param merchant_pub merchant public key
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
- * #GNUNET_NO if nothing was found
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*wire_lookup_deposit_wtid)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_contract_terms,
@@ -1782,11 +1752,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param deposit_serial_id row in the deposits table for which this is
aggregation data
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient errors
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_aggregation_tracking)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_WireTransferIdentifierRawP
*wtid,
@@ -1803,11 +1771,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param end_date when does the fee end being valid
* @param wire_fee how high is the wire transfer fee
* @param master_sig signature over the above by the exchange master key
- * @return #GNUNET_OK on success or if the record exists,
- * #GNUNET_NO on transient errors,
- * #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_wire_fee)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *wire_method,
@@ -1816,7 +1782,7 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_Amount *wire_fee,
const struct TALER_MasterSignatureP *master_sig);
-
+
/**
* Obtain wire fee from database.
*
@@ -1828,10 +1794,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee
* @param[out] master_sig signature over the above by the exchange master key
- * @return #GNUNET_OK on success, #GNUNET_NO if no fee is known
- * #GNUNET_SYSERR on failure
+ * @return query status of the transaction
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_wire_fee) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
@@ -1851,11 +1816,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param now timestamp based on which we decide expiration
* @param rec function to call on expired reserves
* @param rec_cls closure for @a rec
- * @return #GNUNET_SYSERR on database error
- * #GNUNET_NO if there are no expired non-empty reserves
- * #GNUNET_OK on success
+ * @return transaction status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_expired_reserves)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
struct GNUNET_TIME_Absolute now,
@@ -1874,10 +1837,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param wtid identifier for the wire transfer
* @param amount_with_fee amount we charged to the reserve
* @param closing_fee how high is the closing fee
- * @return #GNUNET_OK on success, #GNUNET_NO if the record exists,
- * #GNUNET_SYSERR on failure
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_reserve_closed)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1896,9 +1858,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param type type of the wire transfer (i.e. "sepa")
* @param buf buffer with wire transfer preparation data
* @param buf_size number of bytes in @a buf
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return query status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*wire_prepare_data_insert)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
@@ -1912,9 +1874,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls closure
* @param session database connection
* @param rowid which entry to mark as finished
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*wire_prepare_data_mark_finished)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t rowid);
@@ -1928,11 +1890,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*wire_prepare_data_get)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
TALER_EXCHANGEDB_WirePreparationIterator cb,
@@ -1963,10 +1923,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param wtid subject of the wire transfer
* @param wire_account details about the receiver account of the wire
transfer
* @param amount amount that was transmitted
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*store_wire_transfer_out)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
struct GNUNET_TIME_Absolute date,
@@ -2004,10 +1963,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_deposits_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t serial_id,
@@ -2023,10 +1981,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_refreshs_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t serial_id,
@@ -2043,10 +2000,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_refunds_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t serial_id,
@@ -2063,10 +2019,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_reserves_in_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session
*session,
uint64_t serial_id,
@@ -2082,11 +2037,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id highest serial ID to exclude (select strictly larger)
* @param cb function to call on each result
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if no records were found
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_reserves_out_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session
*session,
uint64_t serial_id,
@@ -2103,11 +2056,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id lowest serial ID to include (select larger or equal)
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_wire_out_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t serial_id,
@@ -2124,11 +2075,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id lowest serial ID to include (select larger or equal)
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_payback_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
uint64_t serial_id,
@@ -2145,11 +2094,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param serial_id lowest serial ID to include (select larger or equal)
* @param cb function to call
* @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*select_reserve_closed_above_serial_id)(void *cls,
struct TALER_EXCHANGEDB_Session
*session,
uint64_t serial_id,
@@ -2175,11 +2122,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param receiver_account_details who should receive the funds
* @param h_blind_ev hash of the blinded coin's envelope (must match
reserves_out entry)
* @param now timestamp to store
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on DB errors
+ * @return transaction result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_payback_request)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -2199,11 +2144,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session a session
* @param h_blind_ev hash of the blinded coin
* @param[out] reserve_pub set to information about the reserve (on success
only)
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if there are no entries,
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_reserve_by_h_blind)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_blind_ev,
@@ -2218,11 +2161,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session a session
* @param denom_pub_hash hash of the revoked denomination key
* @param master_sig signature affirming the revocation
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if the entry already exists (transaction must be
rolled back!)
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_denomination_revocation)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode
*denom_pub_hash,
@@ -2238,11 +2179,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param denom_pub_hash hash of the revoked denomination key
* @param[out] master_sig signature affirming the revocation
* @param[out] rowid row where the information is stored
- * @return #GNUNET_OK on success,
- * #GNUNET_NO no such entry exists
- * #GNUNET_SYSERR on DB errors
+ * @return transaction status code
*/
- int
+ enum GNUNET_DB_QueryStatus
(*get_denomination_revocation)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *denom_pub_hash,
diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h
index 7d8757d..3df1e60 100644
--- a/src/include/taler_fakebank_lib.h
+++ b/src/include/taler_fakebank_lib.h
@@ -62,8 +62,29 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h);
/**
+ * Tell the fakebank to create another wire transfer.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param subject wire transfer subject to use
+ * @param exchange_base_url exchange URL
+ * @return serial_id of the transfer
+ */
+uint64_t
+TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
+ uint64_t debit_account,
+ uint64_t credit_account,
+ const struct TALER_Amount *amount,
+ const char *subject,
+ const char *exchange_base_url);
+
+
+
+/**
* Check that the @a want_amount was transferred from the @a
- * want_debit to the @a want_credit account. If so, set the @a wtid
+ * want_debit to the @a want_credit account. If so, set the @a subject
* to the transfer identifier and remove the transaction from the
* list. If the transaction was not recorded, return #GNUNET_SYSERR.
*
@@ -73,7 +94,7 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h);
* @param want_debit account that should have been credited
* @param exchange_base_url expected base URL of the exchange,
* i.e. "https://example.com/"; may include a port
- * @param[out] wtid set to the wire transfer identifier
+ * @param[out] subject set to the wire transfer identifier
* @return #GNUNET_OK on success
*/
int
@@ -82,7 +103,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
uint64_t want_debit,
uint64_t want_credit,
const char *exchange_base_url,
- struct TALER_WireTransferIdentifierRawP *wtid);
+ char **subject);
/**
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index 9699e68..181c501 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -173,6 +173,11 @@
*/
#define TALER_SIGNATURE_MERCHANT_PAYMENT_OK 1104
+/**
+ * Signature where the merchant confirms a refund increase
+ */
+#define TALER_SIGNATURE_MERCHANT_REFUND_OK 1105
+
/*********************/
/* Wallet signatures */
@@ -1270,6 +1275,25 @@ struct TALER_ReserveCloseConfirmationPS
};
+/**
+ * Used by the merchant to confirm with a signature that the refund has
+ * been successfully done. Even though the frontend doesn't usually do crypto,
+ * this signature may turn useful in court.
+ */
+struct TALER_MerchantRefundConfirmationPS
+{
+ /**
+ * Set TALER_SIGNATURE_REFUND_OK.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hashed order id; in case frontend wants to check it.
+ */
+ struct GNUNET_HashCode h_order_id GNUNET_PACKED;
+
+};
+
GNUNET_NETWORK_STRUCT_END
#endif
diff --git a/src/json/Makefile.am b/src/json/Makefile.am
index 6f71b87..d26f731 100644
--- a/src/json/Makefile.am
+++ b/src/json/Makefile.am
@@ -13,7 +13,7 @@ libtalerjson_la_SOURCES = \
json.c \
json_helper.c
libtalerjson_la_LDFLAGS = \
- -version-info 0:0:0 \
+ -version-info 1:0:1 \
-export-dynamic -no-undefined
libtalerjson_la_LIBADD = \
-lgnunetjson \
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index eb9ffe4..c05f756 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -37,7 +37,7 @@ libtalerutil_wallet_la_LIBADD = \
$(XLIB)
libtalerutil_wallet_la_LDFLAGS = \
- -version-info 0:0:0 \
+ -version-info 1:0:1 \
-export-dynamic -no-undefined
libtalerutil_la_SOURCES = \
diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am
index da1b8b5..f8fb9d0 100644
--- a/src/wire/Makefile.am
+++ b/src/wire/Makefile.am
@@ -17,7 +17,10 @@ EXTRA_DIST = \
wire-sepa.conf \
wire-test.conf \
test_wire_plugin.conf \
- test_wire_plugin_transactions_test.conf
+ test_wire_plugin_transactions_test.conf \
+ test_wire_plugin_key.priv \
+ test_wire_plugin_test.json \
+ test_wire_plugin_sepa.json
plugindir = $(libdir)/taler
--
To stop receiving notification emails like this one, please contact
address@hidden
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] [taler-exchange] branch stable updated (850d84a -> e140b41),
gnunet <=