summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--README.1st19
-rwxr-xr-xbootstrap2
-rw-r--r--configure.ac2
m---------contrib/gana0
-rw-r--r--contrib/pp/en/0.epubbin13772 -> 14983 bytes
-rw-r--r--contrib/pp/en/0.html65
-rw-r--r--contrib/pp/en/0.pdfbin81244 -> 77445 bytes
-rw-r--r--contrib/pp/en/0.txt43
-rw-r--r--contrib/pp/en/0.xml57
-rw-r--r--contrib/pp/locale/de/LC_MESSAGES/pp.po248
-rw-r--r--contrib/pp/pp.rst42
-rw-r--r--contrib/tos/en/0.epubbin16629 -> 17320 bytes
-rw-r--r--contrib/tos/en/0.html163
-rw-r--r--contrib/tos/en/0.pdfbin99859 -> 82060 bytes
-rw-r--r--contrib/tos/en/0.txt112
-rw-r--r--contrib/tos/en/0.xml97
-rw-r--r--contrib/tos/locale/de/LC_MESSAGES/tos.po442
-rw-r--r--contrib/tos/tos.rst2
-rw-r--r--debian/.gitignore3
-rw-r--r--debian/changelog12
-rw-r--r--doc/cbdc-es/cbdc-es.tex1283
-rw-r--r--doc/cbdc-es/cbdc.bib566
-rw-r--r--doc/cbdc-es/deposito.pdfbin0 -> 96830 bytes
-rw-r--r--doc/cbdc-es/eshyphexh.tex1367
-rw-r--r--doc/cbdc-es/graphic-es.odpbin0 -> 145669 bytes
-rw-r--r--doc/cbdc-es/retirada.pdfbin0 -> 62191 bytes
-rw-r--r--doc/cbdc-es/taler_figure_1_dora_SPANISH.jpgbin0 -> 44235 bytes
-rw-r--r--doc/cbdc-es/taler_figure_2_dora_SPANISH.jpgbin0 -> 50323 bytes
-rw-r--r--src/auditor/Makefile.am3
-rw-r--r--src/auditor/report-lib.c20
-rw-r--r--src/auditor/report-lib.h4
-rw-r--r--src/auditor/taler-auditor-dbinit.c1
-rw-r--r--src/auditor/taler-auditor-httpd.c3
-rw-r--r--src/auditor/taler-auditor-httpd_deposit-confirmation.c48
-rw-r--r--src/auditor/taler-auditor-sync.c2
-rw-r--r--src/auditor/taler-helper-auditor-aggregation.c90
-rw-r--r--src/auditor/taler-helper-auditor-coins.c288
-rw-r--r--src/auditor/taler-helper-auditor-deposits.c22
-rw-r--r--src/auditor/taler-helper-auditor-reserves.c16
-rw-r--r--src/auditor/taler-helper-auditor-wire.c127
-rwxr-xr-xsrc/auditor/test-auditor.sh4
-rwxr-xr-xsrc/auditor/test-revocation.sh25
-rw-r--r--src/auditordb/auditor-0001.sql50
-rw-r--r--src/auditordb/plugin_auditordb_postgres.c25
-rw-r--r--src/auditordb/test_auditordb.c45
-rw-r--r--src/bank-lib/bank_api_credit.c11
-rw-r--r--src/bank-lib/bank_api_debit.c9
-rw-r--r--src/bank-lib/fakebank.c148
-rw-r--r--src/bank-lib/taler-exchange-wire-gateway-client.c9
-rwxr-xr-xsrc/bank-lib/test_bank.sh6
-rw-r--r--src/benchmark/taler-aggregator-benchmark.c129
-rw-r--r--src/benchmark/taler-bank-benchmark.c1
-rw-r--r--src/curl/curl.c18
-rw-r--r--src/exchange-tools/taler-auditor-offline.c27
-rw-r--r--src/exchange-tools/taler-exchange-dbinit.c3
-rw-r--r--src/exchange-tools/taler-exchange-offline.c101
-rw-r--r--src/exchange/Makefile.am4
-rw-r--r--src/exchange/exchange.conf30
-rw-r--r--src/exchange/taler-exchange-aggregator.c106
-rw-r--r--src/exchange/taler-exchange-closer.c23
-rw-r--r--src/exchange/taler-exchange-httpd.c349
-rw-r--r--src/exchange/taler-exchange-httpd.h104
-rw-r--r--src/exchange/taler-exchange-httpd_auditors.c6
-rw-r--r--src/exchange/taler-exchange-httpd_auditors.h2
-rw-r--r--src/exchange/taler-exchange-httpd_db.c2
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c139
-rw-r--r--src/exchange/taler-exchange-httpd_deposits_get.c94
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c277
-rw-r--r--src/exchange/taler-exchange-httpd_keys.h24
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-check.c448
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-check.h49
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-proof.c714
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-proof.h49
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-wallet.c156
-rw-r--r--src/exchange/taler-exchange-httpd_kyc-wallet.h45
-rw-r--r--src/exchange/taler-exchange-httpd_link.c8
-rw-r--r--src/exchange/taler-exchange-httpd_management.h2
-rw-r--r--src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c2
-rw-r--r--src/exchange/taler-exchange-httpd_management_post_keys.c128
-rw-r--r--src/exchange/taler-exchange-httpd_melt.c6
-rw-r--r--src/exchange/taler-exchange-httpd_recoup.c71
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c28
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c2
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_get.c3
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c46
-rw-r--r--src/exchange/taler-exchange-httpd_responses.h4
-rw-r--r--src/exchange/taler-exchange-httpd_transfers_get.c87
-rw-r--r--src/exchange/taler-exchange-httpd_wire.c6
-rw-r--r--src/exchange/taler-exchange-httpd_withdraw.c152
-rw-r--r--src/exchangedb/Makefile.am22
-rw-r--r--src/exchangedb/bench-db-postgres.conf7
-rw-r--r--src/exchangedb/bench_db.c110
-rw-r--r--src/exchangedb/benchmark-0000.sql293
-rw-r--r--src/exchangedb/benchmark-0001.sql (renamed from src/exchangedb/drop0003.sql)15
-rw-r--r--src/exchangedb/drop0001.sql18
-rw-r--r--src/exchangedb/drop0002.sql31
-rw-r--r--src/exchangedb/exchange-0001.sql398
-rw-r--r--src/exchangedb/exchange-0002.sql473
-rw-r--r--src/exchangedb/exchange-0003.sql75
-rw-r--r--src/exchangedb/irbt_callbacks.c97
-rw-r--r--src/exchangedb/lrbt_callbacks.c319
-rw-r--r--src/exchangedb/plugin_exchangedb_common.c13
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c5634
-rw-r--r--src/exchangedb/test-exchange-db-postgres.conf2
-rw-r--r--src/exchangedb/test_exchangedb.c674
-rw-r--r--src/include/Makefile.am1
-rw-r--r--src/include/taler_amount_lib.h3
-rw-r--r--src/include/taler_auditor_service.h10
-rw-r--r--src/include/taler_auditordb_plugin.h41
-rw-r--r--src/include/taler_crypto_lib.h797
-rw-r--r--src/include/taler_curl_lib.h2
-rw-r--r--src/include/taler_exchange_service.h577
-rw-r--r--src/include/taler_exchangedb_plugin.h555
-rw-r--r--src/include/taler_extensions.h72
-rw-r--r--src/include/taler_fakebank_lib.h6
-rw-r--r--src/include/taler_json_lib.h60
-rw-r--r--src/include/taler_pq_lib.h82
-rw-r--r--src/include/taler_signatures.h170
-rw-r--r--src/include/taler_testing_lib.h1234
-rw-r--r--src/include/taler_util.h23
-rw-r--r--src/json/Makefile.am1
-rw-r--r--src/json/json.c184
-rw-r--r--src/json/json_helper.c302
-rw-r--r--src/json/json_pack.c88
-rw-r--r--src/json/json_wire.c64
-rw-r--r--src/json/test_json.c40
-rw-r--r--src/lib/Makefile.am3
-rw-r--r--src/lib/auditor_api_deposit_confirmation.c105
-rw-r--r--src/lib/auditor_api_exchanges.c15
-rw-r--r--src/lib/exchange_api_auditor_add_denomination.c2
-rw-r--r--src/lib/exchange_api_common.c91
-rw-r--r--src/lib/exchange_api_deposit.c462
-rw-r--r--src/lib/exchange_api_deposits_get.c141
-rw-r--r--src/lib/exchange_api_handle.c128
-rw-r--r--src/lib/exchange_api_handle.h4
-rw-r--r--src/lib/exchange_api_kyc_check.c297
-rw-r--r--src/lib/exchange_api_kyc_proof.c215
-rw-r--r--src/lib/exchange_api_kyc_wallet.c235
-rw-r--r--src/lib/exchange_api_link.c46
-rw-r--r--src/lib/exchange_api_management_get_keys.c50
-rw-r--r--src/lib/exchange_api_management_revoke_denomination_key.c2
-rw-r--r--src/lib/exchange_api_melt.c29
-rw-r--r--src/lib/exchange_api_recoup.c16
-rw-r--r--src/lib/exchange_api_refresh_common.c631
-rw-r--r--src/lib/exchange_api_refresh_common.h101
-rw-r--r--src/lib/exchange_api_refreshes_reveal.c45
-rw-r--r--src/lib/exchange_api_refund.c98
-rw-r--r--src/lib/exchange_api_transfers_get.c8
-rw-r--r--src/lib/exchange_api_wire.c8
-rw-r--r--src/lib/exchange_api_withdraw.c92
-rw-r--r--src/lib/exchange_api_withdraw2.c36
-rw-r--r--src/mhd/mhd_responses.c6
-rw-r--r--src/mhd/mhd_run.c11
-rw-r--r--src/pq/pq_query_helper.c355
-rw-r--r--src/pq/pq_result_helper.c396
-rw-r--r--src/sq/sq_query_helper.c1
-rw-r--r--src/sq/sq_result_helper.c14
-rw-r--r--src/testing/.gitignore1
-rw-r--r--src/testing/Makefile.am50
-rw-r--r--src/testing/test_auditor_api.c57
-rw-r--r--src/testing/test_auditor_api_version.c3
-rw-r--r--src/testing/test_bank_api.c3
-rw-r--r--src/testing/test_bank_api_twisted.c2
-rw-r--r--src/testing/test_exchange_api.c11
-rw-r--r--src/testing/test_exchange_api_keys_cherry_picking.c3
-rw-r--r--src/testing/test_exchange_api_overlapping_keys_bug.c3
-rw-r--r--src/testing/test_exchange_api_revocation.c6
-rw-r--r--src/testing/test_exchange_api_twisted.c11
-rw-r--r--src/testing/test_exchange_management_api.c3
-rw-r--r--src/testing/test_kyc_api.c291
-rw-r--r--src/testing/test_kyc_api.conf157
-rw-r--r--src/testing/test_taler_exchange_wirewatch.c2
-rw-r--r--src/testing/testing_api_cmd_auditor_add.c23
-rw-r--r--src/testing/testing_api_cmd_auditor_add_denom_sig.c25
-rw-r--r--src/testing/testing_api_cmd_auditor_del.c23
-rw-r--r--src/testing/testing_api_cmd_auditor_deposit_confirmation.c94
-rw-r--r--src/testing/testing_api_cmd_auditor_exec_auditor.c11
-rw-r--r--src/testing/testing_api_cmd_auditor_exec_auditor_dbinit.c9
-rw-r--r--src/testing/testing_api_cmd_bank_admin_add_incoming.c76
-rw-r--r--src/testing/testing_api_cmd_bank_admin_check.c31
-rw-r--r--src/testing/testing_api_cmd_bank_check.c52
-rw-r--r--src/testing/testing_api_cmd_bank_history_credit.c97
-rw-r--r--src/testing/testing_api_cmd_bank_history_debit.c98
-rw-r--r--src/testing/testing_api_cmd_bank_transfer.c40
-rw-r--r--src/testing/testing_api_cmd_batch.c48
-rw-r--r--src/testing/testing_api_cmd_change_auth.c28
-rw-r--r--src/testing/testing_api_cmd_deposit.c234
-rw-r--r--src/testing/testing_api_cmd_deposits_get.c69
-rw-r--r--src/testing/testing_api_cmd_exec_aggregator.c44
-rw-r--r--src/testing/testing_api_cmd_exec_auditor-offline.c163
-rw-r--r--src/testing/testing_api_cmd_exec_closer.c33
-rw-r--r--src/testing/testing_api_cmd_exec_transfer.c14
-rw-r--r--src/testing/testing_api_cmd_exec_wirewatch.c15
-rw-r--r--src/testing/testing_api_cmd_insert_deposit.c132
-rw-r--r--src/testing/testing_api_cmd_kyc_check_get.c252
-rw-r--r--src/testing/testing_api_cmd_kyc_proof.c249
-rw-r--r--src/testing/testing_api_cmd_kyc_wallet_get.c266
-rw-r--r--src/testing/testing_api_cmd_oauth.c384
-rw-r--r--src/testing/testing_api_cmd_offline_sign_fees.c14
-rw-r--r--src/testing/testing_api_cmd_offline_sign_keys.c5
-rw-r--r--src/testing/testing_api_cmd_recoup.c29
-rw-r--r--src/testing/testing_api_cmd_refresh.c217
-rw-r--r--src/testing/testing_api_cmd_refund.c33
-rw-r--r--src/testing/testing_api_cmd_revoke.c15
-rw-r--r--src/testing/testing_api_cmd_rewind.c40
-rw-r--r--src/testing/testing_api_cmd_serialize_keys.c22
-rw-r--r--src/testing/testing_api_cmd_set_wire_fee.c23
-rw-r--r--src/testing/testing_api_cmd_stat.c62
-rw-r--r--src/testing/testing_api_cmd_status.c29
-rw-r--r--src/testing/testing_api_cmd_transfer_get.c80
-rw-r--r--src/testing/testing_api_cmd_twister_exec_client.c270
-rw-r--r--src/testing/testing_api_cmd_wire_add.c23
-rw-r--r--src/testing/testing_api_cmd_wire_del.c23
-rw-r--r--src/testing/testing_api_cmd_withdraw.c200
-rw-r--r--src/testing/testing_api_helpers_bank.c16
-rw-r--r--src/testing/testing_api_helpers_exchange.c73
-rw-r--r--src/testing/testing_api_loop.c110
-rw-r--r--src/testing/testing_api_trait_amount.c76
-rw-r--r--src/testing/testing_api_trait_blinding_key.c77
-rw-r--r--src/testing/testing_api_trait_cmd.c80
-rw-r--r--src/testing/testing_api_trait_coin_priv.c78
-rw-r--r--src/testing/testing_api_trait_contract.c74
-rw-r--r--src/testing/testing_api_trait_denom_pub.c77
-rw-r--r--src/testing/testing_api_trait_denom_sig.c79
-rw-r--r--src/testing/testing_api_trait_exchange_pub.c77
-rw-r--r--src/testing/testing_api_trait_exchange_sig.c77
-rw-r--r--src/testing/testing_api_trait_fresh_coin.c77
-rw-r--r--src/testing/testing_api_trait_json.c165
-rw-r--r--src/testing/testing_api_trait_merchant_key.c127
-rw-r--r--src/testing/testing_api_trait_number.c229
-rw-r--r--src/testing/testing_api_trait_process.c82
-rw-r--r--src/testing/testing_api_trait_reserve_history.c76
-rw-r--r--src/testing/testing_api_trait_reserve_priv.c76
-rw-r--r--src/testing/testing_api_trait_reserve_pub.c78
-rw-r--r--src/testing/testing_api_trait_string.c231
-rw-r--r--src/testing/testing_api_trait_time.c120
-rw-r--r--src/testing/testing_api_trait_uuid.c117
-rw-r--r--src/testing/testing_api_trait_wtid.c76
-rw-r--r--src/testing/testing_api_traits.c25
-rw-r--r--src/util/.gitignore1
-rw-r--r--src/util/Makefile.am18
-rw-r--r--src/util/auditor_signatures.c4
-rw-r--r--src/util/config.c14
-rw-r--r--src/util/crypto.c148
-rw-r--r--src/util/crypto_helper_common.c51
-rw-r--r--src/util/crypto_helper_common.h41
-rw-r--r--src/util/crypto_helper_denom.c735
-rw-r--r--src/util/crypto_helper_esign.c533
-rw-r--r--src/util/crypto_helper_rsa.c629
-rw-r--r--src/util/crypto_wire.c95
-rw-r--r--src/util/denom.c527
-rw-r--r--src/util/exchange_signatures.c69
-rw-r--r--src/util/extension_age_restriction.c180
-rw-r--r--src/util/mhd.c2
-rw-r--r--src/util/offline_signatures.c26
-rw-r--r--src/util/os_installation.c2
-rw-r--r--src/util/payto.c84
-rw-r--r--src/util/secmod_common.c447
-rw-r--r--src/util/secmod_common.h199
-rw-r--r--src/util/secmod_signatures.c16
-rw-r--r--src/util/taler-crypto-worker.c281
-rw-r--r--src/util/taler-exchange-secmod-eddsa.c1359
-rw-r--r--src/util/taler-exchange-secmod-rsa.c1549
-rw-r--r--src/util/taler-exchange-secmod-rsa.h14
-rw-r--r--src/util/test_crypto.c53
-rw-r--r--src/util/test_helper_eddsa.c154
-rw-r--r--src/util/test_helper_eddsa.conf1
-rw-r--r--src/util/test_helper_rsa.c330
-rw-r--r--src/util/util.c9
-rw-r--r--src/util/wallet_signatures.c99
271 files changed, 23705 insertions, 15601 deletions
diff --git a/Makefile.am b/Makefile.am
index 4530bdb2..0bfe4ee0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,4 +24,5 @@ src/include/taler_error_codes.h: contrib/gana/gnu-taler-error-codes/registry.rec
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = build-aux/config.rpath \
- AUTHORS
+ AUTHORS \
+ README.1st
diff --git a/README.1st b/README.1st
new file mode 100644
index 00000000..e1925d7e
--- /dev/null
+++ b/README.1st
@@ -0,0 +1,19 @@
+Building Taler
+==============
+
+Contributions are welcome. Please submit bugs you find to
+https://bugs.taler.net/ or our bugs mailinglist. Submit patches via E-Mail to
+taler@gnu.org, formatted with `git format-patch`.
+
+In order to run the unit tests by hand (instead of using "make check"),
+you need to set the environment variable "TALER_PREFIX" to the
+directory where Taler's libraries are installed.
+Before running any testcases, you must complete the installation.
+
+Quick summary:
+
+$ ./configure --prefix=$SOMEWHERE
+$ make
+$ make install
+$ export $GNUNET_PREFIX=$SOMEWHERE
+$ make check
diff --git a/bootstrap b/bootstrap
index 90cb5133..65b71c7d 100755
--- a/bootstrap
+++ b/bootstrap
@@ -11,7 +11,7 @@ fi
echo "$0: Updating submodules"
echo | git submodule update --init
-./contrib/gana-update.sh
+./contrib/gana.sh
# This is more portable than `which' but comes with
# the caveat of not(?) properly working on busybox's ash:
diff --git a/configure.ac b/configure.ac
index d68aea9c..6995b9b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@
#
#
AC_PREREQ([2.69])
-AC_INIT([taler-exchange], [0.8.4], [taler-bug@gnunet.org])
+AC_INIT([taler-exchange], [0.8.5], [taler-bug@gnunet.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR([src/util/util.c])
AC_CONFIG_HEADERS([taler_config.h])
diff --git a/contrib/gana b/contrib/gana
-Subproject 0272caa8ff8ee7553d035d29fb19d01866df43e
+Subproject b7320181c5e0d95c6f2e2a9e5c53dce0bc1a35a
diff --git a/contrib/pp/en/0.epub b/contrib/pp/en/0.epub
index 49669a4a..7e9e5343 100644
--- a/contrib/pp/en/0.epub
+++ b/contrib/pp/en/0.epub
Binary files differ
diff --git a/contrib/pp/en/0.html b/contrib/pp/en/0.html
index 1e73557f..ce704535 100644
--- a/contrib/pp/en/0.html
+++ b/contrib/pp/en/0.html
@@ -1,17 +1,16 @@
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+<html lang="en">
<head>
- <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy &#8212; Taler Privacy Policy</title>
- <link rel="stylesheet" href="_static/epub.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
- <script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
- <script type="text/javascript" src="_static/language_data.js"></script>
+ <link rel="stylesheet" href="_static/epub.css" type="text/css" />
+ <script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
+ <script src="_static/jquery.js"></script>
+ <script src="_static/underscore.js"></script>
+ <script src="_static/doctools.js"></script>
</head><body>
<div class="document">
@@ -60,9 +59,9 @@ and retain information with a strict business need. That being said, when
using our Services, we inherently have to collect the following information:</p>
<blockquote>
<div><ul class="simple">
-<li>Bank account details necessary when receiving funds from you to top-up your wallet or to transfer funds to you when you are being paid via Taler. At the current experimental stage, only the pseudonym and password you entered in the bank demonstrator is stored.</li>
-<li>The amounts being withdrawn or deposited, with associated unique transaction identifiers and cryptographic signatures authorizing the transaction. Note that for purchases, we cannot identify the buyer from the collected data, so when you spend money, we only receive non-personal information.</li>
-<li>When you contact us. We may collect certain information if you choose to contact us, for example to report a bug or other error with the Taler Wallet. This may include contact information such as your name, email address or phone number depending on the method you choose to contact us.</li>
+<li><p>Bank account details necessary when receiving funds from you to top-up your wallet or to transfer funds to you when you are being paid via Taler. At the current experimental stage, only the pseudonym and password you entered in the bank demonstrator is stored.</p></li>
+<li><p>The amounts being withdrawn or deposited, with associated unique transaction identifiers and cryptographic signatures authorizing the transaction. Note that for purchases, we cannot identify the buyer from the collected data, so when you spend money, we only receive non-personal information.</p></li>
+<li><p>When you contact us. We may collect certain information if you choose to contact us, for example to report a bug or other error with the Taler Wallet. This may include contact information such as your name, email address or phone number depending on the method you choose to contact us.</p></li>
</ul>
</div></blockquote>
</div>
@@ -71,9 +70,9 @@ using our Services, we inherently have to collect the following information:</p>
<p>We may process your information for the following reasons:</p>
<blockquote>
<div><ul class="simple">
-<li>to transfer money as specified by our users (Taler transactions);</li>
-<li>to assist government entities in linking income to the underlying contract</li>
-<li>to support you using the Taler Wallet or to improve our Services</li>
+<li><p>to transfer money as specified by our users (Taler transactions);</p></li>
+<li><p>to assist government entities in linking income to the underlying contract as required by law and local regulations</p></li>
+<li><p>to support you using the Taler Wallet or to improve our Services</p></li>
</ul>
</div></blockquote>
</div>
@@ -96,8 +95,7 @@ errors with the Taler Wallet that you reported.</p>
<p>We may provide your Personal Information to our employees, contractors,
agents, service providers, and designees (“Agents”) to enable them to perform
certain services for us exclusively, including: improvement and maintenance of
-our software and Services. By accepting this Privacy Policy, as outlined
-above, you consent to any such transfer.</p>
+our software and Services.</p>
</div>
<div class="section" id="protection-of-us-and-others">
<h2>Protection of us and others<a class="headerlink" href="#protection-of-us-and-others" title="Permalink to this headline">¶</a></h2>
@@ -118,6 +116,38 @@ and keep your data accurate. Any data that is no longer needed for purposes
specified in the “How We Use the Information We Gather” section will be
deleted after ninety (90) days.</p>
</div>
+<div class="section" id="what-are-your-data-protection-rights">
+<h2>What are your data protection rights?<a class="headerlink" href="#what-are-your-data-protection-rights" title="Permalink to this headline">¶</a></h2>
+<p>Anastasis would like to make sure you are fully aware of all of your
+data protection rights. Every user is entitled to the following:</p>
+<dl class="simple">
+<dt><strong>The right to access</strong>: You have the right to request Anastasis for</dt><dd><p>copies of your personal data. We may charge you a small fee for this
+service.</p>
+</dd>
+</dl>
+<p><strong>The right to rectification</strong>: You have the right to request that
+Anastasis correct any information you believe is inaccurate. You also
+have the right to request Anastasis to complete information you
+believe is incomplete. The right to erasure - You have the right to
+request that Anastasis erase your personal data, under certain
+conditions.</p>
+<dl class="simple">
+<dt><strong>The right to restrict processing</strong>: You have the right to request</dt><dd><p>that Anastasis restrict the processing of your personal data, under
+certain conditions.</p>
+</dd>
+<dt><strong>The right to object to processing</strong>: You have the right to object to</dt><dd><p>Anastasis’s processing of your personal data, under certain
+conditions.</p>
+</dd>
+<dt><strong>The right to data portability</strong>: You have the right to request that</dt><dd><p>Anastasis transfer the data that we have collected to another
+organization, or directly to you, under certain conditions.</p>
+</dd>
+</dl>
+<p>If you make a request, we have one month to respond to you. If you
+would like to exercise any of these rights, please contact us at our
+email: <a class="reference external" href="mailto:privacy&#37;&#52;&#48;taler-systems&#46;com">privacy<span>&#64;</span>taler-systems<span>&#46;</span>com</a></p>
+<p>You can always contact your local data protection authority to enforce
+your rights.</p>
+</div>
<div class="section" id="data-retention">
<h2>Data retention<a class="headerlink" href="#data-retention" title="Permalink to this headline">¶</a></h2>
<p>If you uninstall the Taler Wallet mobile applications from your device, or
@@ -171,6 +201,7 @@ privacy practices that are not addressed in this Privacy Statement.</p>
</div>
+ <div class="clearer"></div>
</div>
</div>
</div>
diff --git a/contrib/pp/en/0.pdf b/contrib/pp/en/0.pdf
index f594aa3b..842a8306 100644
--- a/contrib/pp/en/0.pdf
+++ b/contrib/pp/en/0.pdf
Binary files differ
diff --git a/contrib/pp/en/0.txt b/contrib/pp/en/0.txt
index a5194d32..d6e42faf 100644
--- a/contrib/pp/en/0.txt
+++ b/contrib/pp/en/0.txt
@@ -79,7 +79,7 @@ We may process your information for the following reasons:
* to transfer money as specified by our users (Taler transactions);
* to assist government entities in linking income to the underlying
- contract
+ contract as required by law and local regulations
* to support you using the Taler Wallet or to improve our Services
@@ -109,9 +109,7 @@ Agents or third party partners
We may provide your Personal Information to our employees,
contractors, agents, service providers, and designees (“Agents”) to
enable them to perform certain services for us exclusively, including:
-improvement and maintenance of our software and Services. By accepting
-this Privacy Policy, as outlined above, you consent to any such
-transfer.
+improvement and maintenance of our software and Services.
Protection of us and others
@@ -138,6 +136,43 @@ purposes specified in the “How We Use the Information We Gather”
section will be deleted after ninety (90) days.
+What are your data protection rights?
+=====================================
+
+Anastasis would like to make sure you are fully aware of all of your
+data protection rights. Every user is entitled to the following:
+
+**The right to access**: You have the right to request Anastasis for
+ copies of your personal data. We may charge you a small fee for
+ this service.
+
+**The right to rectification**: You have the right to request that
+Anastasis correct any information you believe is inaccurate. You also
+have the right to request Anastasis to complete information you
+believe is incomplete. The right to erasure - You have the right to
+request that Anastasis erase your personal data, under certain
+conditions.
+
+**The right to restrict processing**: You have the right to request
+ that Anastasis restrict the processing of your personal data, under
+ certain conditions.
+
+**The right to object to processing**: You have the right to object to
+ Anastasis's processing of your personal data, under certain
+ conditions.
+
+**The right to data portability**: You have the right to request that
+ Anastasis transfer the data that we have collected to another
+ organization, or directly to you, under certain conditions.
+
+If you make a request, we have one month to respond to you. If you
+would like to exercise any of these rights, please contact us at our
+email: privacy@taler-systems.com
+
+You can always contact your local data protection authority to enforce
+your rights.
+
+
Data retention
==============
diff --git a/contrib/pp/en/0.xml b/contrib/pp/en/0.xml
index 3050c92b..d4400542 100644
--- a/contrib/pp/en/0.xml
+++ b/contrib/pp/en/0.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE document PUBLIC "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML" "http://docutils.sourceforge.net/docs/ref/docutils.dtd">
-<!-- Generated by Docutils 0.14 -->
-<document source="/home/grothoff/research/taler/exchange/contrib/pp/pp.rst">
+<!-- Generated by Docutils 0.16 -->
+<document source="/research/taler/exchange/contrib/pp/pp.rst">
<section ids="privacy-policy" names="privacy\ policy">
<title>Privacy Policy</title>
<paragraph>Last Updated: 11.12.2019</paragraph>
@@ -64,7 +64,7 @@
<paragraph>to transfer money as specified by our users (Taler transactions);</paragraph>
</list_item>
<list_item>
- <paragraph>to assist government entities in linking income to the underlying contract</paragraph>
+ <paragraph>to assist government entities in linking income to the underlying contract as required by law and local regulations</paragraph>
</list_item>
<list_item>
<paragraph>to support you using the Taler Wallet or to improve our Services</paragraph>
@@ -91,8 +91,7 @@
<paragraph>We may provide your Personal Information to our employees, contractors,
agents, service providers, and designees (“Agents”) to enable them to perform
certain services for us exclusively, including: improvement and maintenance of
- our software and Services. By accepting this Privacy Policy, as outlined
- above, you consent to any such transfer.</paragraph>
+ our software and Services.</paragraph>
</section>
<section ids="protection-of-us-and-others" names="protection\ of\ us\ and\ others">
<title>Protection of us and others</title>
@@ -113,6 +112,54 @@
specified in the “How We Use the Information We Gather” section will be
deleted after ninety (90) days.</paragraph>
</section>
+ <section ids="what-are-your-data-protection-rights" names="what\ are\ your\ data\ protection\ rights?">
+ <title>What are your data protection rights?</title>
+ <paragraph>Anastasis would like to make sure you are fully aware of all of your
+ data protection rights. Every user is entitled to the following:</paragraph>
+ <definition_list>
+ <definition_list_item>
+ <term><strong>The right to access</strong>: You have the right to request Anastasis for</term>
+ <definition>
+ <paragraph>copies of your personal data. We may charge you a small fee for this
+ service.</paragraph>
+ </definition>
+ </definition_list_item>
+ </definition_list>
+ <paragraph><strong>The right to rectification</strong>: You have the right to request that
+ Anastasis correct any information you believe is inaccurate. You also
+ have the right to request Anastasis to complete information you
+ believe is incomplete. The right to erasure - You have the right to
+ request that Anastasis erase your personal data, under certain
+ conditions.</paragraph>
+ <definition_list>
+ <definition_list_item>
+ <term><strong>The right to restrict processing</strong>: You have the right to request</term>
+ <definition>
+ <paragraph>that Anastasis restrict the processing of your personal data, under
+ certain conditions.</paragraph>
+ </definition>
+ </definition_list_item>
+ <definition_list_item>
+ <term><strong>The right to object to processing</strong>: You have the right to object to</term>
+ <definition>
+ <paragraph>Anastasis’s processing of your personal data, under certain
+ conditions.</paragraph>
+ </definition>
+ </definition_list_item>
+ <definition_list_item>
+ <term><strong>The right to data portability</strong>: You have the right to request that</term>
+ <definition>
+ <paragraph>Anastasis transfer the data that we have collected to another
+ organization, or directly to you, under certain conditions.</paragraph>
+ </definition>
+ </definition_list_item>
+ </definition_list>
+ <paragraph>If you make a request, we have one month to respond to you. If you
+ would like to exercise any of these rights, please contact us at our
+ email: <reference refuri="mailto:privacy@taler-systems.com">privacy@taler-systems.com</reference></paragraph>
+ <paragraph>You can always contact your local data protection authority to enforce
+ your rights.</paragraph>
+ </section>
<section ids="data-retention" names="data\ retention">
<title>Data retention</title>
<paragraph>If you uninstall the Taler Wallet mobile applications from your device, or
diff --git a/contrib/pp/locale/de/LC_MESSAGES/pp.po b/contrib/pp/locale/de/LC_MESSAGES/pp.po
index b9544472..b97b7037 100644
--- a/contrib/pp/locale/de/LC_MESSAGES/pp.po
+++ b/contrib/pp/locale/de/LC_MESSAGES/pp.po
@@ -1,21 +1,20 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2014-2020 Taler Systems SA (GPLv3+ or GFDL 1.3+)
# This file is distributed under the same license as the pp package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: pp 0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-02-07 00:51+0100\n"
+"POT-Creation-Date: 2021-09-30 21:41+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.6.0\n"
#: ../../pp.rst:2
msgid "Privacy Policy"
@@ -26,14 +25,7 @@ msgid "Last Updated: 11.12.2019"
msgstr ""
#: ../../pp.rst:6
-msgid ""
-"This Privacy Policy describes the policies and procedures of Taler "
-"Systems SA (“we,” “our,” or “us”) pertaining to the collection, use, and "
-"disclosure of your information on our sites and related mobile "
-"applications and products we offer (the “Services” or “Taler Wallet”). "
-"This Privacy Statement applies to your personal data when you use our "
-"Services, and does not apply to online websites or services that we do "
-"not own or control."
+msgid "This Privacy Policy describes the policies and procedures of Taler Systems SA (“we,” “our,” or “us”) pertaining to the collection, use, and disclosure of your information on our sites and related mobile applications and products we offer (the “Services” or “Taler Wallet”). This Privacy Statement applies to your personal data when you use our Services, and does not apply to online websites or services that we do not own or control."
msgstr ""
#: ../../pp.rst:15
@@ -41,17 +33,7 @@ msgid "Overview"
msgstr ""
#: ../../pp.rst:17
-msgid ""
-"Your privacy is important to us. We follow a few fundamental principles: "
-"We don’t ask you for personally identifiable information (defined below)."
-" That being said, your contact information, such as your phone number, "
-"social media handle, or email address (depending on how you contact us), "
-"may be collected when you communicate with us, for example to report a "
-"bug or other error related to the Taler Wallet. We don’t share your "
-"information with third parties except when strictly required to deliver "
-"you our Services and products, or to comply with the law. If you have any"
-" questions or concerns about this policy, please reach out to us at "
-"privacy@taler-systems.net."
+msgid "Your privacy is important to us. We follow a few fundamental principles: We don’t ask you for personally identifiable information (defined below). That being said, your contact information, such as your phone number, social media handle, or email address (depending on how you contact us), may be collected when you communicate with us, for example to report a bug or other error related to the Taler Wallet. We don’t share your information with third parties except when strictly required to deliver you our Services and products, or to comply with the law. If you have any questions or concerns about this policy, please reach out to us at privacy@taler-systems.net."
msgstr ""
#: ../../pp.rst:29
@@ -59,9 +41,7 @@ msgid "How you accept this policy"
msgstr ""
#: ../../pp.rst:31
-msgid ""
-"By using our Services or visiting our sites, you agree to the use, "
-"disclosure, and procedures outlined in this Privacy Policy."
+msgid "By using our Services or visiting our sites, you agree to the use, disclosure, and procedures outlined in this Privacy Policy."
msgstr ""
#: ../../pp.rst:36
@@ -69,49 +49,23 @@ msgid "What personal information do we collect from our users?"
msgstr ""
#: ../../pp.rst:38
-msgid ""
-"The information we collect from you falls into two categories: (i) "
-"personally identifiable information (i.e., data that could potentially "
-"identify you as an individual) (“Personal Information”), and (ii) non-"
-"personally identifiable information (i.e., information that cannot be "
-"used to identify who you are) (“Non-Personal Information”). This Privacy "
-"Policy covers both categories and will tell you how we might collect and "
-"use each type."
+msgid "The information we collect from you falls into two categories: (i) personally identifiable information (i.e., data that could potentially identify you as an individual) (“Personal Information”), and (ii) non-personally identifiable information (i.e., information that cannot be used to identify who you are) (“Non-Personal Information”). This Privacy Policy covers both categories and will tell you how we might collect and use each type."
msgstr ""
#: ../../pp.rst:45
-msgid ""
-"We do our best to not collect any Personal Information from Taler Wallet "
-"users. We believe that the Taler Wallet never transmits personal "
-"information to our services without at least clear implied consent, and "
-"we only process and retain information with a strict business need. That "
-"being said, when using our Services, we inherently have to collect the "
-"following information:"
+msgid "We do our best to not collect any Personal Information from Taler Wallet users. We believe that the Taler Wallet never transmits personal information to our services without at least clear implied consent, and we only process and retain information with a strict business need. That being said, when using our Services, we inherently have to collect the following information:"
msgstr ""
#: ../../pp.rst:51
-msgid ""
-"Bank account details necessary when receiving funds from you to top-up "
-"your wallet or to transfer funds to you when you are being paid via "
-"Taler. At the current experimental stage, only the pseudonym and password"
-" you entered in the bank demonstrator is stored."
+msgid "Bank account details necessary when receiving funds from you to top-up your wallet or to transfer funds to you when you are being paid via Taler. At the current experimental stage, only the pseudonym and password you entered in the bank demonstrator is stored."
msgstr ""
#: ../../pp.rst:53
-msgid ""
-"The amounts being withdrawn or deposited, with associated unique "
-"transaction identifiers and cryptographic signatures authorizing the "
-"transaction. Note that for purchases, we cannot identify the buyer from "
-"the collected data, so when you spend money, we only receive non-personal"
-" information."
+msgid "The amounts being withdrawn or deposited, with associated unique transaction identifiers and cryptographic signatures authorizing the transaction. Note that for purchases, we cannot identify the buyer from the collected data, so when you spend money, we only receive non-personal information."
msgstr ""
#: ../../pp.rst:55
-msgid ""
-"When you contact us. We may collect certain information if you choose to "
-"contact us, for example to report a bug or other error with the Taler "
-"Wallet. This may include contact information such as your name, email "
-"address or phone number depending on the method you choose to contact us."
+msgid "When you contact us. We may collect certain information if you choose to contact us, for example to report a bug or other error with the Taler Wallet. This may include contact information such as your name, email address or phone number depending on the method you choose to contact us."
msgstr ""
#: ../../pp.rst:59
@@ -127,7 +81,7 @@ msgid "to transfer money as specified by our users (Taler transactions);"
msgstr ""
#: ../../pp.rst:64
-msgid "to assist government entities in linking income to the underlying contract"
+msgid "to assist government entities in linking income to the underlying contract as required by law and local regulations"
msgstr ""
#: ../../pp.rst:65
@@ -139,26 +93,15 @@ msgid "How we share and use the information we gather"
msgstr ""
#: ../../pp.rst:71
-msgid ""
-"We may share your Personal Data or other information about you only if "
-"you are a merchant receiving income, with your bank, to the degree "
-"necessary to execute the payment."
+msgid "We may share your Personal Data or other information about you only if you are a merchant receiving income, with your bank, to the degree necessary to execute the payment."
msgstr ""
#: ../../pp.rst:75
-msgid ""
-"We retain Personal Data to transfer funds to the accounts designated by "
-"our users. We may retain Personal Data only for as long as mandated by "
-"law and required for the wire transfers."
+msgid "We retain Personal Data to transfer funds to the accounts designated by our users. We may retain Personal Data only for as long as mandated by law and required for the wire transfers."
msgstr ""
#: ../../pp.rst:79
-msgid ""
-"We primarily use the limited information we receive directly from you to "
-"enhance the Taler Wallet. Some ways we may use your Personal Information "
-"are to: Contact you when necessary to respond to your comments, answer "
-"your questions, or obtain additional information on issues related to "
-"bugs or errors with the Taler Wallet that you reported."
+msgid "We primarily use the limited information we receive directly from you to enhance the Taler Wallet. Some ways we may use your Personal Information are to: Contact you when necessary to respond to your comments, answer your questions, or obtain additional information on issues related to bugs or errors with the Taler Wallet that you reported."
msgstr ""
#: ../../pp.rst:87
@@ -166,118 +109,113 @@ msgid "Agents or third party partners"
msgstr ""
#: ../../pp.rst:89
-msgid ""
-"We may provide your Personal Information to our employees, contractors, "
-"agents, service providers, and designees (“Agents”) to enable them to "
-"perform certain services for us exclusively, including: improvement and "
-"maintenance of our software and Services. By accepting this Privacy "
-"Policy, as outlined above, you consent to any such transfer."
+msgid "We may provide your Personal Information to our employees, contractors, agents, service providers, and designees (“Agents”) to enable them to perform certain services for us exclusively, including: improvement and maintenance of our software and Services."
msgstr ""
-#: ../../pp.rst:97
+#: ../../pp.rst:96
msgid "Protection of us and others"
msgstr ""
-#: ../../pp.rst:99
-msgid ""
-"We reserve the right to access, read, preserve, and disclose any "
-"information that we reasonably believe is necessary to comply with the "
-"law or a court order."
+#: ../../pp.rst:98
+msgid "We reserve the right to access, read, preserve, and disclose any information that we reasonably believe is necessary to comply with the law or a court order."
msgstr ""
-#: ../../pp.rst:105
+#: ../../pp.rst:104
msgid "What personal information can I access or change?"
msgstr ""
-#: ../../pp.rst:107
-msgid ""
-"You can request access to the information we have collected from you. You"
-" can do this by contacting us at privacy@taler-systems.net. We will make "
-"sure to provide you with a copy of the data we process about you. To "
-"comply with your request, we may ask you to verify your identity. We will"
-" fulfill your request by sending your copy electronically. For any "
-"subsequent access request, we may charge you with an administrative fee. "
-"If you believe that the information we have collected is incorrect, you "
-"are welcome to contact us so we can update it and keep your data "
-"accurate. Any data that is no longer needed for purposes specified in the"
-" “How We Use the Information We Gather” section will be deleted after "
-"ninety (90) days."
-msgstr ""
-
-#: ../../pp.rst:120
+#: ../../pp.rst:106
+msgid "You can request access to the information we have collected from you. You can do this by contacting us at privacy@taler-systems.net. We will make sure to provide you with a copy of the data we process about you. To comply with your request, we may ask you to verify your identity. We will fulfill your request by sending your copy electronically. For any subsequent access request, we may charge you with an administrative fee. If you believe that the information we have collected is incorrect, you are welcome to contact us so we can update it and keep your data accurate. Any data that is no longer needed for purposes specified in the “How We Use the Information We Gather” section will be deleted after ninety (90) days."
+msgstr ""
+
+#: ../../pp.rst:119
+msgid "What are your data protection rights?"
+msgstr ""
+
+#: ../../pp.rst:121
+msgid "Anastasis would like to make sure you are fully aware of all of your data protection rights. Every user is entitled to the following:"
+msgstr ""
+
+#: ../../pp.rst:126
+msgid "**The right to access**: You have the right to request Anastasis for"
+msgstr ""
+
+#: ../../pp.rst:125
+msgid "copies of your personal data. We may charge you a small fee for this service."
+msgstr ""
+
+#: ../../pp.rst:128
+msgid "**The right to rectification**: You have the right to request that Anastasis correct any information you believe is inaccurate. You also have the right to request Anastasis to complete information you believe is incomplete. The right to erasure - You have the right to request that Anastasis erase your personal data, under certain conditions."
+msgstr ""
+
+#: ../../pp.rst:137
+msgid "**The right to restrict processing**: You have the right to request"
+msgstr ""
+
+#: ../../pp.rst:136
+msgid "that Anastasis restrict the processing of your personal data, under certain conditions."
+msgstr ""
+
+#: ../../pp.rst:141
+msgid "**The right to object to processing**: You have the right to object to"
+msgstr ""
+
+#: ../../pp.rst:140
+msgid "Anastasis's processing of your personal data, under certain conditions."
+msgstr ""
+
+#: ../../pp.rst:145
+msgid "**The right to data portability**: You have the right to request that"
+msgstr ""
+
+#: ../../pp.rst:144
+msgid "Anastasis transfer the data that we have collected to another organization, or directly to you, under certain conditions."
+msgstr ""
+
+#: ../../pp.rst:147
+msgid "If you make a request, we have one month to respond to you. If you would like to exercise any of these rights, please contact us at our email: privacy@taler-systems.com"
+msgstr ""
+
+#: ../../pp.rst:151
+msgid "You can always contact your local data protection authority to enforce your rights."
+msgstr ""
+
+#: ../../pp.rst:156
msgid "Data retention"
msgstr ""
-#: ../../pp.rst:122
-msgid ""
-"If you uninstall the Taler Wallet mobile applications from your device, "
-"or request that your information be deleted, we still may retain some "
-"information that you have provided to us to maintain the Taler Wallet or "
-"to comply with relevant laws."
+#: ../../pp.rst:158
+msgid "If you uninstall the Taler Wallet mobile applications from your device, or request that your information be deleted, we still may retain some information that you have provided to us to maintain the Taler Wallet or to comply with relevant laws."
msgstr ""
-#: ../../pp.rst:129
+#: ../../pp.rst:165
msgid "Data security"
msgstr ""
-#: ../../pp.rst:131
-msgid ""
-"We are committed to making sure your information is protected. We employ "
-"several physical and electronic safeguards to keep your information safe,"
-" including encrypted user passwords, two factor verification and "
-"authentication on passwords where possible, and securing connections with"
-" industry standard transport layer security. You are also welcome to "
-"contact us using GnuPG encrypted e-mail. Even with all these precautions,"
-" we cannot fully guarantee against the access, disclosure, alteration, or"
-" deletion of data through events, including but not limited to hardware "
-"or software failure or unauthorized use. Any information that you provide"
-" to us is done so entirely at your own risk."
+#: ../../pp.rst:167
+msgid "We are committed to making sure your information is protected. We employ several physical and electronic safeguards to keep your information safe, including encrypted user passwords, two factor verification and authentication on passwords where possible, and securing connections with industry standard transport layer security. You are also welcome to contact us using GnuPG encrypted e-mail. Even with all these precautions, we cannot fully guarantee against the access, disclosure, alteration, or deletion of data through events, including but not limited to hardware or software failure or unauthorized use. Any information that you provide to us is done so entirely at your own risk."
msgstr ""
-#: ../../pp.rst:144
+#: ../../pp.rst:180
msgid "Changes and updates to privacy policy"
msgstr ""
-#: ../../pp.rst:146
-msgid ""
-"We reserve the right to update and revise this privacy policy at any "
-"time. We occasionally review this Privacy Policy to make sure it complies"
-" with applicable laws and conforms to changes in our business. We may "
-"need to update this Privacy Policy, and we reserve the right to do so at "
-"any time. If we do revise this Privacy Policy, we will update the "
-"“Effective Date” at the bottom of this page so that you can tell if it "
-"has changed since your last visit. As we generally do not collect contact"
-" information and also do not track your visits, we will not be able to "
-"notify you directly. However, the Taler Wallet may inform you about a "
-"change in the privacy policy once it detects that the policy has changed."
-" Please review this Privacy Policy regularly to ensure that you are aware"
-" of its terms. Any use of our Services after an amendment to our Privacy "
-"Policy constitutes your acceptance to the revised or amended agreement."
-msgstr ""
-
-#: ../../pp.rst:162
+#: ../../pp.rst:182
+msgid "We reserve the right to update and revise this privacy policy at any time. We occasionally review this Privacy Policy to make sure it complies with applicable laws and conforms to changes in our business. We may need to update this Privacy Policy, and we reserve the right to do so at any time. If we do revise this Privacy Policy, we will update the “Effective Date” at the bottom of this page so that you can tell if it has changed since your last visit. As we generally do not collect contact information and also do not track your visits, we will not be able to notify you directly. However, the Taler Wallet may inform you about a change in the privacy policy once it detects that the policy has changed. Please review this Privacy Policy regularly to ensure that you are aware of its terms. Any use of our Services after an amendment to our Privacy Policy constitutes your acceptance to the revised or amended agreement."
+msgstr ""
+
+#: ../../pp.rst:198
msgid "International users and visitors"
msgstr ""
-#: ../../pp.rst:164
-msgid ""
-"Our Services are hosted in Switzerland. If you are a user accessing the "
-"Services from the European Union, Asia, US, or any other region with laws"
-" or regulations governing personal data collection, use, and disclosure "
-"that differ from Swiss laws, please be advised that through your "
-"continued use of the Services, which is governed by Swiss law, you are "
-"transferring your Personal Information to Switzerland and you consent to "
-"that transfer."
+#: ../../pp.rst:200
+msgid "Our Services are hosted in Switzerland. If you are a user accessing the Services from the European Union, Asia, US, or any other region with laws or regulations governing personal data collection, use, and disclosure that differ from Swiss laws, please be advised that through your continued use of the Services, which is governed by Swiss law, you are transferring your Personal Information to Switzerland and you consent to that transfer."
msgstr ""
-#: ../../pp.rst:173
+#: ../../pp.rst:209
msgid "Questions"
msgstr ""
-#: ../../pp.rst:175
-msgid ""
-"Please contact us at privacy@taler-systems.net if you have questions "
-"about our privacy practices that are not addressed in this Privacy "
-"Statement."
+#: ../../pp.rst:211
+msgid "Please contact us at privacy@taler-systems.net if you have questions about our privacy practices that are not addressed in this Privacy Statement."
msgstr ""
-
diff --git a/contrib/pp/pp.rst b/contrib/pp/pp.rst
index d37c10c2..e6d003d8 100644
--- a/contrib/pp/pp.rst
+++ b/contrib/pp/pp.rst
@@ -61,7 +61,7 @@ How we collect and process information
We may process your information for the following reasons:
* to transfer money as specified by our users (Taler transactions);
- * to assist government entities in linking income to the underlying contract
+ * to assist government entities in linking income to the underlying contract as required by law and local regulations
* to support you using the Taler Wallet or to improve our Services
@@ -89,8 +89,7 @@ Agents or third party partners
We may provide your Personal Information to our employees, contractors,
agents, service providers, and designees (“Agents”) to enable them to perform
certain services for us exclusively, including: improvement and maintenance of
-our software and Services. By accepting this Privacy Policy, as outlined
-above, you consent to any such transfer.
+our software and Services.
Protection of us and others
@@ -116,6 +115,43 @@ specified in the “How We Use the Information We Gather” section will be
deleted after ninety (90) days.
+What are your data protection rights?
+-------------------------------------
+
+Anastasis would like to make sure you are fully aware of all of your
+data protection rights. Every user is entitled to the following:
+
+**The right to access**: You have the right to request Anastasis for
+ copies of your personal data. We may charge you a small fee for this
+ service.
+
+**The right to rectification**: You have the right to request that
+Anastasis correct any information you believe is inaccurate. You also
+have the right to request Anastasis to complete information you
+believe is incomplete. The right to erasure - You have the right to
+request that Anastasis erase your personal data, under certain
+conditions.
+
+**The right to restrict processing**: You have the right to request
+ that Anastasis restrict the processing of your personal data, under
+ certain conditions.
+
+**The right to object to processing**: You have the right to object to
+ Anastasis's processing of your personal data, under certain
+ conditions.
+
+**The right to data portability**: You have the right to request that
+ Anastasis transfer the data that we have collected to another
+ organization, or directly to you, under certain conditions.
+
+If you make a request, we have one month to respond to you. If you
+would like to exercise any of these rights, please contact us at our
+email: privacy@taler-systems.com
+
+You can always contact your local data protection authority to enforce
+your rights.
+
+
Data retention
--------------
diff --git a/contrib/tos/en/0.epub b/contrib/tos/en/0.epub
index 5acb8dfc..2e6c6609 100644
--- a/contrib/tos/en/0.epub
+++ b/contrib/tos/en/0.epub
Binary files differ
diff --git a/contrib/tos/en/0.html b/contrib/tos/en/0.html
index 07a3ab40..6e7f66f5 100644
--- a/contrib/tos/en/0.html
+++ b/contrib/tos/en/0.html
@@ -1,17 +1,16 @@
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+<html lang="en">
<head>
- <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Terms Of Service &#8212; Taler Terms of Service</title>
- <link rel="stylesheet" href="_static/epub.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
- <script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
- <script type="text/javascript" src="_static/jquery.js"></script>
- <script type="text/javascript" src="_static/underscore.js"></script>
- <script type="text/javascript" src="_static/doctools.js"></script>
- <script type="text/javascript" src="_static/language_data.js"></script>
+ <link rel="stylesheet" href="_static/epub.css" type="text/css" />
+ <script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
+ <script src="_static/jquery.js"></script>
+ <script src="_static/underscore.js"></script>
+ <script src="_static/doctools.js"></script>
</head><body>
<div class="document">
@@ -39,25 +38,25 @@ carefully.</p>
<h3>Highlights:<a class="headerlink" href="#highlights" title="Permalink to this headline">¶</a></h3>
<blockquote>
<div><ul class="simple">
-<li>You are responsible for keeping the data in your Taler Wallet at all times
+<li><p>You are responsible for keeping the data in your Taler Wallet at all times
under your control. Any losses arising from you not being in control of
-your private information are your problem.</li>
-<li>We will try to transfer funds we hold in escrow for our users to any legal
+your private information are your problem.</p></li>
+<li><p>We will try to transfer funds we hold in escrow for our users to any legal
recipient to the best of our ability within the limitations of the law and
our implementation. However, the Services offered today are highly
-experimental and the set of recipients of funds is severely restricted.</li>
-<li>For our Services, we may charge transaction fees. The specific fee structure
+experimental and the set of recipients of funds is severely restricted.</p></li>
+<li><p>For our Services, we may charge transaction fees. The specific fee structure
is provided based on the Taler protocol and should be shown to you when you
withdraw electronic coins using a Taler Wallet. You agree and understand
-that the Taler protocol allows for the fee structure to change.</li>
-<li>You agree to not intentionally overwhelm our systems with requests and
-follow responsible disclosure if you find security issues in our services.</li>
-<li>We cannot be held accountable for our Services not being available due to
+that the Taler protocol allows for the fee structure to change.</p></li>
+<li><p>You agree to not intentionally overwhelm our systems with requests and
+follow responsible disclosure if you find security issues in our services.</p></li>
+<li><p>We cannot be held accountable for our Services not being available due to
circumstances beyond our control. If we modify or terminate our services,
we will try to give you the opportunity to recover your funds. However,
given the experimental state of the Services today, this may not be
possible. You are strongly advised to limit your use of the Service
-to small-scale experiments expecting total loss of all funds.</li>
+to small-scale experiments expecting total loss of all funds.</p></li>
</ul>
</div></blockquote>
<p>These terms outline approved uses of our Services. The Services and these
@@ -103,6 +102,14 @@ situations where we are unable to make transfers at all or lead to incorrect
transfers that cannot be reversed. We will only refuse to execute transfers if
the transfers are prohibited by a competent legal authority and we are ordered
to do so.</p>
+<p>When using our Services, you agree to not take any action that intentionally
+imposes an unreasonable load on our infrastructure. If you find security
+problems in our Services, you agree to first report them to
+<a class="reference external" href="mailto:security&#37;&#52;&#48;taler-systems&#46;com">security<span>&#64;</span>taler-systems<span>&#46;</span>com</a> and grant us the right to publish your report. We
+warrant that we will ourselves publicly disclose any issues reported within 3
+months, and that we will not prosecute anyone reporting security issues if
+they did not exploit the issue beyond a proof-of-concept, and followed the
+above responsible disclosure practice.</p>
</div>
<div class="section" id="fees">
<h2>Fees<a class="headerlink" href="#fees" title="Permalink to this headline">¶</a></h2>
@@ -120,15 +127,12 @@ We reserve the right to provide different types of rewards to users either in
the form of discount for our Services or in any other form at our discretion
and without prior notice to you.</p>
</div>
-<div class="section" id="eligibility">
-<h2>Eligibility<a class="headerlink" href="#eligibility" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="eligibility-and-financial-self-responsibility">
+<h2>Eligibility and Financial self-responsibility<a class="headerlink" href="#eligibility-and-financial-self-responsibility" title="Permalink to this headline">¶</a></h2>
<p>To be eligible to use our Services, you must be able to form legally binding
contracts or have the permission of your legal guardian. By using our
Services, you represent and warrant that you meet all eligibility requirements
that we outline in these Terms.</p>
-</div>
-<div class="section" id="financial-self-responsibility">
-<h2>Financial self-responsibility<a class="headerlink" href="#financial-self-responsibility" title="Permalink to this headline">¶</a></h2>
<p>You will be responsible for maintaining the availability, integrity and
confidentiality of the data stored in your wallet. When you setup a Taler
Wallet, you are strongly advised to follow the precautionary measures offered
@@ -149,17 +153,6 @@ of Taler Systems SA. You are welcome to use the name in relation to processing
payments using the Taler protocol, assuming your use is compatible with an
official release from the GNU Project that is not older than two years.</p>
</div>
-<div class="section" id="your-use-of-our-services">
-<h2>Your use of our services<a class="headerlink" href="#your-use-of-our-services" title="Permalink to this headline">¶</a></h2>
-<p>When using our Services, you agree to not take any action that intentionally
-imposes an unreasonable load on our infrastructure. If you find security
-problems in our Services, you agree to first report them to
-<a class="reference external" href="mailto:security&#37;&#52;&#48;taler-systems&#46;com">security<span>&#64;</span>taler-systems<span>&#46;</span>com</a> and grant us the right to publish your report. We
-warrant that we will ourselves publicly disclose any issues reported within 3
-months, and that we will not prosecute anyone reporting security issues if
-they did not exploit the issue beyond a proof-of-concept, and followed the
-above responsible disclosure practice.</p>
-</div>
<div class="section" id="limitation-of-liability-disclaimer-of-warranties">
<h2>Limitation of liability &amp; disclaimer of warranties<a class="headerlink" href="#limitation-of-liability-disclaimer-of-warranties" title="Permalink to this headline">¶</a></h2>
<p>You understand and agree that we have no control over, and no duty to take any
@@ -172,22 +165,19 @@ Taler Wallet, including, but not limited to your Taler Wallet coins or backup
encryption keys. You release us from all liability related to any losses,
damages, or claims arising from:</p>
<ol class="loweralpha simple">
-<li>user error such as forgotten passwords, incorrectly constructed
-transactions;</li>
-<li>server failure or data loss;</li>
-<li>unauthorized access to the Taler Wallet application;</li>
-<li>bugs or other errors in the Taler Wallet software; and</li>
-<li>any unauthorized third party activities, including, but not limited to,
+<li><p>user error such as forgotten passwords, incorrectly constructed
+transactions;</p></li>
+<li><p>server failure or data loss;</p></li>
+<li><p>unauthorized access to the Taler Wallet application;</p></li>
+<li><p>bugs or other errors in the Taler Wallet software; and</p></li>
+<li><p>any unauthorized third party activities, including, but not limited to,
the use of viruses, phishing, brute forcing, or other means of attack
against the Taler Wallet. We make no representations concerning any
-Third Party Content contained in or accessed through our Services.</li>
+Third Party Content contained in or accessed through our Services.</p></li>
</ol>
<p>Any other terms, conditions, warranties, or representations associated with
such content, are solely between you and such organizations and/or
individuals.</p>
-</div>
-<div class="section" id="limitation-of-liability">
-<h2>Limitation of liability<a class="headerlink" href="#limitation-of-liability" title="Permalink to this headline">¶</a></h2>
<p>To the fullest extent permitted by applicable law, in no event will we or any
of our officers, directors, representatives, agents, servants, counsel,
employees, consultants, lawyers, and other personnel authorized to act,
@@ -195,23 +185,23 @@ acting, or purporting to act on our behalf (collectively the “Taler Parties”
be liable to you under contract, tort, strict liability, negligence, or any
other legal or equitable theory, for:</p>
<ol class="loweralpha simple">
-<li>any lost profits, data loss, cost of procurement of substitute goods or
+<li><p>any lost profits, data loss, cost of procurement of substitute goods or
services, or direct, indirect, incidental, special, punitive, compensatory,
-or consequential damages of any kind whatsoever resulting from:</li>
+or consequential damages of any kind whatsoever resulting from:</p></li>
</ol>
<blockquote>
<div><ol class="lowerroman simple">
-<li>your use of, or conduct in connection with, our services;</li>
-<li>any unauthorized use of your wallet and/or private key due to your
-failure to maintain the confidentiality of your wallet;</li>
-<li>any interruption or cessation of transmission to or from the services; or</li>
-<li>any bugs, viruses, trojan horses, or the like that are found in the Taler
+<li><p>your use of, or conduct in connection with, our services;</p></li>
+<li><p>any unauthorized use of your wallet and/or private key due to your
+failure to maintain the confidentiality of your wallet;</p></li>
+<li><p>any interruption or cessation of transmission to or from the services; or</p></li>
+<li><p>any bugs, viruses, trojan horses, or the like that are found in the Taler
Wallet software or that may be transmitted to or through our services by
-any third party (regardless of the source of origination), or</li>
+any third party (regardless of the source of origination), or</p></li>
</ol>
</div></blockquote>
<ol class="loweralpha simple" start="2">
-<li>any direct damages.</li>
+<li><p>any direct damages.</p></li>
</ol>
<p>These limitations apply regardless of legal theory, whether based on tort,
strict liability, breach of contract, breach of warranty, or any other legal
@@ -219,9 +209,6 @@ theory, and whether or not we were advised of the possibility of such
damages. Some jurisdictions do not allow the exclusion or limitation of
liability for consequential or incidental damages, so the above limitation may
not apply to you.</p>
-</div>
-<div class="section" id="warranty-disclaimer">
-<h2>Warranty disclaimer<a class="headerlink" href="#warranty-disclaimer" title="Permalink to this headline">¶</a></h2>
<p>Our services are provided “as is” and without warranty of any kind. To the
maximum extent permitted by law, we disclaim all representations and
warranties, express or implied, relating to the services and underlying
@@ -238,8 +225,8 @@ implied warranties, so the foregoing disclaimers may not apply to you. This
paragraph gives you specific legal rights and you may also have other legal
rights that vary from state to state.</p>
</div>
-<div class="section" id="indemnity">
-<h2>Indemnity<a class="headerlink" href="#indemnity" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="indemnity-and-time-limitation-on-claims-and-termination">
+<h2>Indemnity and Time limitation on claims and Termination<a class="headerlink" href="#indemnity-and-time-limitation-on-claims-and-termination" title="Permalink to this headline">¶</a></h2>
<p>To the extent permitted by applicable law, you agree to defend, indemnify, and
hold harmless the Taler Parties from and against any and all claims, damages,
obligations, losses, liabilities, costs or debt, and expenses (including, but
@@ -247,30 +234,14 @@ not limited to, attorney’s fees) arising from: (a) your use of and access to
the Services; (b) any feedback or submissions you provide to us concerning the
Taler Wallet; (c) your violation of any term of this Agreement; or (d) your
violation of any law, rule, or regulation, or the rights of any third party.</p>
-</div>
-<div class="section" id="time-limitation-on-claims">
-<h2>Time limitation on claims<a class="headerlink" href="#time-limitation-on-claims" title="Permalink to this headline">¶</a></h2>
<p>You agree that any claim you may have arising out of or related to your
relationship with us must be filed within one year after such claim arises,
otherwise, your claim in permanently barred.</p>
-</div>
-<div class="section" id="governing-law">
-<h2>Governing law<a class="headerlink" href="#governing-law" title="Permalink to this headline">¶</a></h2>
-<p>No matter where you’re located, the laws of Switzerland will govern these
-Terms. If any provisions of these Terms are inconsistent with any applicable
-law, those provisions will be superseded or modified only to the extent such
-provisions are inconsistent. The parties agree to submit to the ordinary
-courts in Zurich, Switzerland for exclusive jurisdiction of any dispute
-arising out of or related to your use of the Services or your breach of these
-Terms.</p>
-</div>
-<div class="section" id="termination">
-<h2>Termination<a class="headerlink" href="#termination" title="Permalink to this headline">¶</a></h2>
<p>In the event of termination concerning your use of our Services, your
obligations under this Agreement will still continue.</p>
</div>
-<div class="section" id="discontinuance-of-services">
-<h2>Discontinuance of services<a class="headerlink" href="#discontinuance-of-services" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="discontinuance-of-services-and-force-majeure">
+<h2>Discontinuance of services and Force majeure<a class="headerlink" href="#discontinuance-of-services-and-force-majeure" title="Permalink to this headline">¶</a></h2>
<p>We may, in our sole discretion and without cost to you, with or without prior
notice, and at any time, modify or discontinue, temporarily or permanently,
any portion of our Services. We will use the Taler protocol’s provisions to
@@ -280,22 +251,6 @@ three months to observe these notifications. We shall not be held responsible
or liable for any loss of funds in the event that we discontinue or depreciate
the Services and your Taler Wallet fails to transfer out the coins within a
three months notification period.</p>
-</div>
-<div class="section" id="no-waiver">
-<h2>No waiver<a class="headerlink" href="#no-waiver" title="Permalink to this headline">¶</a></h2>
-<p>Our failure to exercise or delay in exercising any right, power, or privilege
-under this Agreement shall not operate as a waiver; nor shall any single or
-partial exercise of any right, power, or privilege preclude any other or
-further exercise thereof.</p>
-</div>
-<div class="section" id="severability">
-<h2>Severability<a class="headerlink" href="#severability" title="Permalink to this headline">¶</a></h2>
-<p>If it turns out that any part of this Agreement is invalid, void, or for any
-reason unenforceable, that term will be deemed severable and limited or
-eliminated to the minimum extent necessary.</p>
-</div>
-<div class="section" id="force-majeure">
-<h2>Force majeure<a class="headerlink" href="#force-majeure" title="Permalink to this headline">¶</a></h2>
<p>We shall not be held liable for any delays, failure in performance, or
interruptions of service which result directly or indirectly from any cause or
condition beyond our reasonable control, including but not limited to: any
@@ -306,13 +261,24 @@ services, failure of equipment and/or software, other catastrophe, or any
other occurrence which is beyond our reasonable control and shall not affect
the validity and enforceability of any remaining provisions.</p>
</div>
-<div class="section" id="assignment">
-<h2>Assignment<a class="headerlink" href="#assignment" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="governing-law-waivers-severability-and-assignment">
+<h2>Governing law, Waivers, Severability and Assignment<a class="headerlink" href="#governing-law-waivers-severability-and-assignment" title="Permalink to this headline">¶</a></h2>
+<p>No matter where you’re located, the laws of Switzerland will govern these
+Terms. If any provisions of these Terms are inconsistent with any applicable
+law, those provisions will be superseded or modified only to the extent such
+provisions are inconsistent. The parties agree to submit to the ordinary
+courts in Zurich, Switzerland for exclusive jurisdiction of any dispute
+arising out of or related to your use of the Services or your breach of these
+Terms.</p>
+<p>Our failure to exercise or delay in exercising any right, power, or privilege
+under this Agreement shall not operate as a waiver; nor shall any single or
+partial exercise of any right, power, or privilege preclude any other or
+further exercise thereof.</p>
<p>You agree that we may assign any of our rights and/or transfer, sub-contract,
or delegate any of our obligations under these Terms.</p>
-</div>
-<div class="section" id="entire-agreement">
-<h2>Entire agreement<a class="headerlink" href="#entire-agreement" title="Permalink to this headline">¶</a></h2>
+<p>If it turns out that any part of this Agreement is invalid, void, or for any
+reason unenforceable, that term will be deemed severable and limited or
+eliminated to the minimum extent necessary.</p>
<p>This Agreement sets forth the entire understanding and agreement as to the
subject matter hereof and supersedes any and all prior discussions,
agreements, and understandings of any kind (including, without limitation, any
@@ -328,6 +294,7 @@ message on our contact page at <a class="reference external" href="mailto:legal&
</div>
+ <div class="clearer"></div>
</div>
</div>
</div>
diff --git a/contrib/tos/en/0.pdf b/contrib/tos/en/0.pdf
index ce3f013c..89d39493 100644
--- a/contrib/tos/en/0.pdf
+++ b/contrib/tos/en/0.pdf
Binary files differ
diff --git a/contrib/tos/en/0.txt b/contrib/tos/en/0.txt
index ce0893fa..c124d70e 100644
--- a/contrib/tos/en/0.txt
+++ b/contrib/tos/en/0.txt
@@ -103,6 +103,16 @@ lead to incorrect transfers that cannot be reversed. We will only
refuse to execute transfers if the transfers are prohibited by a
competent legal authority and we are ordered to do so.
+When using our Services, you agree to not take any action that
+intentionally imposes an unreasonable load on our infrastructure. If
+you find security problems in our Services, you agree to first report
+them to security@taler-systems.com and grant us the right to publish
+your report. We warrant that we will ourselves publicly disclose any
+issues reported within 3 months, and that we will not prosecute anyone
+reporting security issues if they did not exploit the issue beyond a
+proof-of-concept, and followed the above responsible disclosure
+practice.
+
Fees
====
@@ -123,18 +133,14 @@ rewards to users either in the form of discount for our Services or in
any other form at our discretion and without prior notice to you.
-Eligibility
-===========
+Eligibility and Financial self-responsibility
+=============================================
To be eligible to use our Services, you must be able to form legally
binding contracts or have the permission of your legal guardian. By
using our Services, you represent and warrant that you meet all
eligibility requirements that we outline in these Terms.
-
-Financial self-responsibility
-=============================
-
You will be responsible for maintaining the availability, integrity
and confidentiality of the data stored in your wallet. When you setup
a Taler Wallet, you are strongly advised to follow the precautionary
@@ -160,20 +166,6 @@ assuming your use is compatible with an official release from the GNU
Project that is not older than two years.
-Your use of our services
-========================
-
-When using our Services, you agree to not take any action that
-intentionally imposes an unreasonable load on our infrastructure. If
-you find security problems in our Services, you agree to first report
-them to security@taler-systems.com and grant us the right to publish
-your report. We warrant that we will ourselves publicly disclose any
-issues reported within 3 months, and that we will not prosecute anyone
-reporting security issues if they did not exploit the issue beyond a
-proof-of-concept, and followed the above responsible disclosure
-practice.
-
-
Limitation of liability & disclaimer of warranties
==================================================
@@ -207,10 +199,6 @@ Any other terms, conditions, warranties, or representations associated
with such content, are solely between you and such organizations
and/or individuals.
-
-Limitation of liability
-=======================
-
To the fullest extent permitted by applicable law, in no event will we
or any of our officers, directors, representatives, agents, servants,
counsel, employees, consultants, lawyers, and other personnel
@@ -246,10 +234,6 @@ possibility of such damages. Some jurisdictions do not allow the
exclusion or limitation of liability for consequential or incidental
damages, so the above limitation may not apply to you.
-
-Warranty disclaimer
-===================
-
Our services are provided "as is" and without warranty of any kind. To
the maximum extent permitted by law, we disclaim all representations
and warranties, express or implied, relating to the services and
@@ -268,8 +252,8 @@ you. This paragraph gives you specific legal rights and you may also
have other legal rights that vary from state to state.
-Indemnity
-=========
+Indemnity and Time limitation on claims and Termination
+=======================================================
To the extent permitted by applicable law, you agree to defend,
indemnify, and hold harmless the Taler Parties from and against any
@@ -281,36 +265,16 @@ feedback or submissions you provide to us concerning the Taler Wallet;
violation of any law, rule, or regulation, or the rights of any third
party.
-
-Time limitation on claims
-=========================
-
You agree that any claim you may have arising out of or related to
your relationship with us must be filed within one year after such
claim arises, otherwise, your claim in permanently barred.
-
-Governing law
-=============
-
-No matter where you’re located, the laws of Switzerland will govern
-these Terms. If any provisions of these Terms are inconsistent with
-any applicable law, those provisions will be superseded or modified
-only to the extent such provisions are inconsistent. The parties agree
-to submit to the ordinary courts in Zurich, Switzerland for exclusive
-jurisdiction of any dispute arising out of or related to your use of
-the Services or your breach of these Terms.
-
-
-Termination
-===========
-
In the event of termination concerning your use of our Services, your
obligations under this Agreement will still continue.
-Discontinuance of services
-==========================
+Discontinuance of services and Force majeure
+============================================
We may, in our sole discretion and without cost to you, with or
without prior notice, and at any time, modify or discontinue,
@@ -323,27 +287,6 @@ of funds in the event that we discontinue or depreciate the Services
and your Taler Wallet fails to transfer out the coins within a three
months notification period.
-
-No waiver
-=========
-
-Our failure to exercise or delay in exercising any right, power, or
-privilege under this Agreement shall not operate as a waiver; nor
-shall any single or partial exercise of any right, power, or privilege
-preclude any other or further exercise thereof.
-
-
-Severability
-============
-
-If it turns out that any part of this Agreement is invalid, void, or
-for any reason unenforceable, that term will be deemed severable and
-limited or eliminated to the minimum extent necessary.
-
-
-Force majeure
-=============
-
We shall not be held liable for any delays, failure in performance, or
interruptions of service which result directly or indirectly from any
cause or condition beyond our reasonable control, including but not
@@ -356,15 +299,28 @@ occurrence which is beyond our reasonable control and shall not affect
the validity and enforceability of any remaining provisions.
-Assignment
-==========
+Governing law, Waivers, Severability and Assignment
+===================================================
+
+No matter where you’re located, the laws of Switzerland will govern
+these Terms. If any provisions of these Terms are inconsistent with
+any applicable law, those provisions will be superseded or modified
+only to the extent such provisions are inconsistent. The parties agree
+to submit to the ordinary courts in Zurich, Switzerland for exclusive
+jurisdiction of any dispute arising out of or related to your use of
+the Services or your breach of these Terms.
+
+Our failure to exercise or delay in exercising any right, power, or
+privilege under this Agreement shall not operate as a waiver; nor
+shall any single or partial exercise of any right, power, or privilege
+preclude any other or further exercise thereof.
You agree that we may assign any of our rights and/or transfer, sub-
contract, or delegate any of our obligations under these Terms.
-
-Entire agreement
-================
+If it turns out that any part of this Agreement is invalid, void, or
+for any reason unenforceable, that term will be deemed severable and
+limited or eliminated to the minimum extent necessary.
This Agreement sets forth the entire understanding and agreement as to
the subject matter hereof and supersedes any and all prior
diff --git a/contrib/tos/en/0.xml b/contrib/tos/en/0.xml
index 75974dc4..ee93a4cc 100644
--- a/contrib/tos/en/0.xml
+++ b/contrib/tos/en/0.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE document PUBLIC "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML" "http://docutils.sourceforge.net/docs/ref/docutils.dtd">
-<!-- Generated by Docutils 0.14 -->
-<document source="/home/grothoff/research/taler/exchange/contrib/tos/tos.rst">
+<!-- Generated by Docutils 0.16 -->
+<document source="/research/taler/exchange/contrib/tos/tos.rst">
<section ids="terms-of-service" names="terms\ of\ service">
<title>Terms Of Service</title>
<paragraph>Last Updated: 12.4.2019</paragraph>
@@ -96,6 +96,14 @@
transfers that cannot be reversed. We will only refuse to execute transfers if
the transfers are prohibited by a competent legal authority and we are ordered
to do so.</paragraph>
+ <paragraph>When using our Services, you agree to not take any action that intentionally
+ imposes an unreasonable load on our infrastructure. If you find security
+ problems in our Services, you agree to first report them to
+ <reference refuri="mailto:security@taler-systems.com">security@taler-systems.com</reference> and grant us the right to publish your report. We
+ warrant that we will ourselves publicly disclose any issues reported within 3
+ months, and that we will not prosecute anyone reporting security issues if
+ they did not exploit the issue beyond a proof-of-concept, and followed the
+ above responsible disclosure practice.</paragraph>
</section>
<section ids="fees" names="fees">
<title>Fees</title>
@@ -113,15 +121,12 @@
the form of discount for our Services or in any other form at our discretion
and without prior notice to you.</paragraph>
</section>
- <section ids="eligibility" names="eligibility">
- <title>Eligibility</title>
+ <section ids="eligibility-and-financial-self-responsibility" names="eligibility\ and\ financial\ self-responsibility">
+ <title>Eligibility and Financial self-responsibility</title>
<paragraph>To be eligible to use our Services, you must be able to form legally binding
contracts or have the permission of your legal guardian. By using our
Services, you represent and warrant that you meet all eligibility requirements
that we outline in these Terms.</paragraph>
- </section>
- <section ids="financial-self-responsibility" names="financial\ self-responsibility">
- <title>Financial self-responsibility</title>
<paragraph>You will be responsible for maintaining the availability, integrity and
confidentiality of the data stored in your wallet. When you setup a Taler
Wallet, you are strongly advised to follow the precautionary measures offered
@@ -142,17 +147,6 @@
payments using the Taler protocol, assuming your use is compatible with an
official release from the GNU Project that is not older than two years.</paragraph>
</section>
- <section ids="your-use-of-our-services" names="your\ use\ of\ our\ services">
- <title>Your use of our services</title>
- <paragraph>When using our Services, you agree to not take any action that intentionally
- imposes an unreasonable load on our infrastructure. If you find security
- problems in our Services, you agree to first report them to
- <reference refuri="mailto:security@taler-systems.com">security@taler-systems.com</reference> and grant us the right to publish your report. We
- warrant that we will ourselves publicly disclose any issues reported within 3
- months, and that we will not prosecute anyone reporting security issues if
- they did not exploit the issue beyond a proof-of-concept, and followed the
- above responsible disclosure practice.</paragraph>
- </section>
<section ids="limitation-of-liability-disclaimer-of-warranties" names="limitation\ of\ liability\ &amp;\ disclaimer\ of\ warranties">
<title>Limitation of liability &amp; disclaimer of warranties</title>
<paragraph>You understand and agree that we have no control over, and no duty to take any
@@ -188,9 +182,6 @@
<paragraph>Any other terms, conditions, warranties, or representations associated with
such content, are solely between you and such organizations and/or
individuals.</paragraph>
- </section>
- <section ids="limitation-of-liability" names="limitation\ of\ liability">
- <title>Limitation of liability</title>
<paragraph>To the fullest extent permitted by applicable law, in no event will we or any
of our officers, directors, representatives, agents, servants, counsel,
employees, consultants, lawyers, and other personnel authorized to act,
@@ -234,9 +225,6 @@
damages. Some jurisdictions do not allow the exclusion or limitation of
liability for consequential or incidental damages, so the above limitation may
not apply to you.</paragraph>
- </section>
- <section ids="warranty-disclaimer" names="warranty\ disclaimer">
- <title>Warranty disclaimer</title>
<paragraph>Our services are provided “as is” and without warranty of any kind. To the
maximum extent permitted by law, we disclaim all representations and
warranties, express or implied, relating to the services and underlying
@@ -253,8 +241,8 @@
paragraph gives you specific legal rights and you may also have other legal
rights that vary from state to state.</paragraph>
</section>
- <section ids="indemnity" names="indemnity">
- <title>Indemnity</title>
+ <section ids="indemnity-and-time-limitation-on-claims-and-termination" names="indemnity\ and\ time\ limitation\ on\ claims\ and\ termination">
+ <title>Indemnity and Time limitation on claims and Termination</title>
<paragraph>To the extent permitted by applicable law, you agree to defend, indemnify, and
hold harmless the Taler Parties from and against any and all claims, damages,
obligations, losses, liabilities, costs or debt, and expenses (including, but
@@ -262,30 +250,14 @@
the Services; (b) any feedback or submissions you provide to us concerning the
Taler Wallet; (c) your violation of any term of this Agreement; or (d) your
violation of any law, rule, or regulation, or the rights of any third party.</paragraph>
- </section>
- <section ids="time-limitation-on-claims" names="time\ limitation\ on\ claims">
- <title>Time limitation on claims</title>
<paragraph>You agree that any claim you may have arising out of or related to your
relationship with us must be filed within one year after such claim arises,
otherwise, your claim in permanently barred.</paragraph>
- </section>
- <section ids="governing-law" names="governing\ law">
- <title>Governing law</title>
- <paragraph>No matter where you’re located, the laws of Switzerland will govern these
- Terms. If any provisions of these Terms are inconsistent with any applicable
- law, those provisions will be superseded or modified only to the extent such
- provisions are inconsistent. The parties agree to submit to the ordinary
- courts in Zurich, Switzerland for exclusive jurisdiction of any dispute
- arising out of or related to your use of the Services or your breach of these
- Terms.</paragraph>
- </section>
- <section ids="termination" names="termination">
- <title>Termination</title>
<paragraph>In the event of termination concerning your use of our Services, your
obligations under this Agreement will still continue.</paragraph>
</section>
- <section ids="discontinuance-of-services" names="discontinuance\ of\ services">
- <title>Discontinuance of services</title>
+ <section ids="discontinuance-of-services-and-force-majeure" names="discontinuance\ of\ services\ and\ force\ majeure">
+ <title>Discontinuance of services and Force majeure</title>
<paragraph>We may, in our sole discretion and without cost to you, with or without prior
notice, and at any time, modify or discontinue, temporarily or permanently,
any portion of our Services. We will use the Taler protocol’s provisions to
@@ -295,22 +267,6 @@
or liable for any loss of funds in the event that we discontinue or depreciate
the Services and your Taler Wallet fails to transfer out the coins within a
three months notification period.</paragraph>
- </section>
- <section ids="no-waiver" names="no\ waiver">
- <title>No waiver</title>
- <paragraph>Our failure to exercise or delay in exercising any right, power, or privilege
- under this Agreement shall not operate as a waiver; nor shall any single or
- partial exercise of any right, power, or privilege preclude any other or
- further exercise thereof.</paragraph>
- </section>
- <section ids="severability" names="severability">
- <title>Severability</title>
- <paragraph>If it turns out that any part of this Agreement is invalid, void, or for any
- reason unenforceable, that term will be deemed severable and limited or
- eliminated to the minimum extent necessary.</paragraph>
- </section>
- <section ids="force-majeure" names="force\ majeure">
- <title>Force majeure</title>
<paragraph>We shall not be held liable for any delays, failure in performance, or
interruptions of service which result directly or indirectly from any cause or
condition beyond our reasonable control, including but not limited to: any
@@ -321,13 +277,24 @@
other occurrence which is beyond our reasonable control and shall not affect
the validity and enforceability of any remaining provisions.</paragraph>
</section>
- <section ids="assignment" names="assignment">
- <title>Assignment</title>
+ <section ids="governing-law-waivers-severability-and-assignment" names="governing\ law,\ waivers,\ severability\ and\ assignment">
+ <title>Governing law, Waivers, Severability and Assignment</title>
+ <paragraph>No matter where you’re located, the laws of Switzerland will govern these
+ Terms. If any provisions of these Terms are inconsistent with any applicable
+ law, those provisions will be superseded or modified only to the extent such
+ provisions are inconsistent. The parties agree to submit to the ordinary
+ courts in Zurich, Switzerland for exclusive jurisdiction of any dispute
+ arising out of or related to your use of the Services or your breach of these
+ Terms.</paragraph>
+ <paragraph>Our failure to exercise or delay in exercising any right, power, or privilege
+ under this Agreement shall not operate as a waiver; nor shall any single or
+ partial exercise of any right, power, or privilege preclude any other or
+ further exercise thereof.</paragraph>
<paragraph>You agree that we may assign any of our rights and/or transfer, sub-contract,
or delegate any of our obligations under these Terms.</paragraph>
- </section>
- <section ids="entire-agreement" names="entire\ agreement">
- <title>Entire agreement</title>
+ <paragraph>If it turns out that any part of this Agreement is invalid, void, or for any
+ reason unenforceable, that term will be deemed severable and limited or
+ eliminated to the minimum extent necessary.</paragraph>
<paragraph>This Agreement sets forth the entire understanding and agreement as to the
subject matter hereof and supersedes any and all prior discussions,
agreements, and understandings of any kind (including, without limitation, any
diff --git a/contrib/tos/locale/de/LC_MESSAGES/tos.po b/contrib/tos/locale/de/LC_MESSAGES/tos.po
index 21aa9c88..e821c9c1 100644
--- a/contrib/tos/locale/de/LC_MESSAGES/tos.po
+++ b/contrib/tos/locale/de/LC_MESSAGES/tos.po
@@ -1,21 +1,20 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2014-2020 Taler Systems SA (GPLv3+ or GFDL 1.3+)
# This file is distributed under the same license as the tos package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: tos 0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-02-07 01:00+0100\n"
+"POT-Creation-Date: 2021-09-30 21:42+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.6.0\n"
#: ../../tos.rst:2
msgid "Terms Of Service"
@@ -26,11 +25,7 @@ msgid "Last Updated: 12.4.2019"
msgstr ""
#: ../../tos.rst:6
-msgid ""
-"Welcome! Taler Systems SA (“we,” “our,” or “us”) provides a payment "
-"service through our Internet presence (collectively the “Services”). "
-"Before using our Services, please read the Terms of Service (the “Terms” "
-"or the “Agreement”) carefully."
+msgid "Welcome! Taler Systems SA (“we,” “our,” or “us”) provides a payment service through our Internet presence (collectively the “Services”). Before using our Services, please read the Terms of Service (the “Terms” or the “Agreement”) carefully."
msgstr ""
#: ../../tos.rst:12
@@ -38,14 +33,7 @@ msgid "Overview"
msgstr ""
#: ../../tos.rst:14
-msgid ""
-"This section provides a brief summary of the highlights of this "
-"Agreement. Please note that when you accept this Agreement, you are "
-"accepting all of the terms and conditions and not just this section. We "
-"and possibly other third parties provide Internet services which interact"
-" with the Taler Wallet’s self-hosted personal payment application. When "
-"using the Taler Wallet to interact with our Services, you are agreeing to"
-" our Terms, so please read carefully."
+msgid "This section provides a brief summary of the highlights of this Agreement. Please note that when you accept this Agreement, you are accepting all of the terms and conditions and not just this section. We and possibly other third parties provide Internet services which interact with the Taler Wallet’s self-hosted personal payment application. When using the Taler Wallet to interact with our Services, you are agreeing to our Terms, so please read carefully."
msgstr ""
#: ../../tos.rst:23
@@ -53,54 +41,27 @@ msgid "Highlights:"
msgstr ""
#: ../../tos.rst:25
-msgid ""
-"You are responsible for keeping the data in your Taler Wallet at all "
-"times under your control. Any losses arising from you not being in "
-"control of your private information are your problem."
+msgid "You are responsible for keeping the data in your Taler Wallet at all times under your control. Any losses arising from you not being in control of your private information are your problem."
msgstr ""
#: ../../tos.rst:28
-msgid ""
-"We will try to transfer funds we hold in escrow for our users to any "
-"legal recipient to the best of our ability within the limitations of the "
-"law and our implementation. However, the Services offered today are "
-"highly experimental and the set of recipients of funds is severely "
-"restricted."
+msgid "We will try to transfer funds we hold in escrow for our users to any legal recipient to the best of our ability within the limitations of the law and our implementation. However, the Services offered today are highly experimental and the set of recipients of funds is severely restricted."
msgstr ""
#: ../../tos.rst:32
-msgid ""
-"For our Services, we may charge transaction fees. The specific fee "
-"structure is provided based on the Taler protocol and should be shown to "
-"you when you withdraw electronic coins using a Taler Wallet. You agree "
-"and understand that the Taler protocol allows for the fee structure to "
-"change."
+msgid "For our Services, we may charge transaction fees. The specific fee structure is provided based on the Taler protocol and should be shown to you when you withdraw electronic coins using a Taler Wallet. You agree and understand that the Taler protocol allows for the fee structure to change."
msgstr ""
#: ../../tos.rst:36
-msgid ""
-"You agree to not intentionally overwhelm our systems with requests and "
-"follow responsible disclosure if you find security issues in our "
-"services."
+msgid "You agree to not intentionally overwhelm our systems with requests and follow responsible disclosure if you find security issues in our services."
msgstr ""
#: ../../tos.rst:38
-msgid ""
-"We cannot be held accountable for our Services not being available due to"
-" circumstances beyond our control. If we modify or terminate our "
-"services, we will try to give you the opportunity to recover your funds. "
-"However, given the experimental state of the Services today, this may not"
-" be possible. You are strongly advised to limit your use of the Service "
-"to small-scale experiments expecting total loss of all funds."
+msgid "We cannot be held accountable for our Services not being available due to circumstances beyond our control. If we modify or terminate our services, we will try to give you the opportunity to recover your funds. However, given the experimental state of the Services today, this may not be possible. You are strongly advised to limit your use of the Service to small-scale experiments expecting total loss of all funds."
msgstr ""
#: ../../tos.rst:45
-msgid ""
-"These terms outline approved uses of our Services. The Services and these"
-" Terms are still at an experimental stage. If you have any questions or "
-"comments related to this Agreement, please send us a message to legal"
-"@taler-systems.com. If you do not agree to this Agreement, you must not "
-"use our Services."
+msgid "These terms outline approved uses of our Services. The Services and these Terms are still at an experimental stage. If you have any questions or comments related to this Agreement, please send us a message to legal@taler-systems.com. If you do not agree to this Agreement, you must not use our Services."
msgstr ""
#: ../../tos.rst:52
@@ -108,19 +69,7 @@ msgid "How you accept this policy"
msgstr ""
#: ../../tos.rst:54
-msgid ""
-"By sending funds to us (to top-up your Taler Wallet), you acknowledge "
-"that you have read, understood, and agreed to these Terms. We reserve the"
-" right to change these Terms at any time. If you disagree with the "
-"change, we may in the future offer you with an easy option to recover "
-"your unspent funds. However, in the current experimental period you "
-"acknowledge that this feature is not yet available, resulting in your "
-"funds being lost unless you accept the new Terms. If you continue to use "
-"our Services other than to recover your unspent funds, your continued use"
-" of our Services following any such change will signify your acceptance "
-"to be bound by the then current Terms. Please check the effective date "
-"above to determine if there have been any changes since you have last "
-"reviewed these Terms."
+msgid "By sending funds to us (to top-up your Taler Wallet), you acknowledge that you have read, understood, and agreed to these Terms. We reserve the right to change these Terms at any time. If you disagree with the change, we may in the future offer you with an easy option to recover your unspent funds. However, in the current experimental period you acknowledge that this feature is not yet available, resulting in your funds being lost unless you accept the new Terms. If you continue to use our Services other than to recover your unspent funds, your continued use of our Services following any such change will signify your acceptance to be bound by the then current Terms. Please check the effective date above to determine if there have been any changes since you have last reviewed these Terms."
msgstr ""
#: ../../tos.rst:67
@@ -128,390 +77,165 @@ msgid "Services"
msgstr ""
#: ../../tos.rst:69
-msgid ""
-"We will try to transfer funds that we hold in escrow for our users to any"
-" legal recipient to the best of our ability and within the limitations of"
-" the law and our implementation. However, the Services offered today are "
-"highly experimental and the set of recipients of funds is severely "
-"restricted. The Taler Wallet can be loaded by exchanging fiat currencies"
-" against electronic coins. We are providing this exchange service. Once "
-"your Taler Wallet is loaded with electronic coins they can be spent for "
-"purchases if the seller is accepting Taler as a means of payment. We are "
-"not guaranteeing that any seller is accepting Taler at all or a "
-"particular seller. The seller or recipient of deposits of electronic "
-"coins must specify the target account, as per the design of the Taler "
-"protocol. They are responsible for following the protocol and specifying "
-"the correct bank account, and are solely liable for any losses that may "
-"arise from specifying the wrong account. We will allow the government to "
-"link wire transfers to the underlying contract hash. It is the "
-"responsibility of recipients to preserve the full contracts and to pay "
-"whatever taxes and charges may be applicable. Technical issues may lead "
-"to situations where we are unable to make transfers at all or lead to "
-"incorrect transfers that cannot be reversed. We will only refuse to "
-"execute transfers if the transfers are prohibited by a competent legal "
-"authority and we are ordered to do so."
-msgstr ""
-
-#: ../../tos.rst:91
-msgid "Fees"
+msgid "We will try to transfer funds that we hold in escrow for our users to any legal recipient to the best of our ability and within the limitations of the law and our implementation. However, the Services offered today are highly experimental and the set of recipients of funds is severely restricted. The Taler Wallet can be loaded by exchanging fiat currencies against electronic coins. We are providing this exchange service. Once your Taler Wallet is loaded with electronic coins they can be spent for purchases if the seller is accepting Taler as a means of payment. We are not guaranteeing that any seller is accepting Taler at all or a particular seller. The seller or recipient of deposits of electronic coins must specify the target account, as per the design of the Taler protocol. They are responsible for following the protocol and specifying the correct bank account, and are solely liable for any losses that may arise from specifying the wrong account. We will allow the government to link wire transfers to the underlying contract hash. It is the responsibility of recipients to preserve the full contracts and to pay whatever taxes and charges may be applicable. Technical issues may lead to situations where we are unable to make transfers at all or lead to incorrect transfers that cannot be reversed. We will only refuse to execute transfers if the transfers are prohibited by a competent legal authority and we are ordered to do so."
msgstr ""
-#: ../../tos.rst:93
-msgid ""
-"You agree to pay the fees for exchanges and withdrawals completed via the"
-" Taler Wallet (\"Fees\") as defined by us, which we may change from time "
-"to time. With the exception of wire transfer fees, Taler transaction fees"
-" are set for any electronic coin at the time of withdrawal and fixed "
-"throughout the validity period of the respective electronic coin. Your "
-"wallet should obtain and display applicable fees when withdrawing funds. "
-"Fees for coins obtained as change may differ from the fees applicable to "
-"the original coin. Wire transfer fees that are independent from "
-"electronic coins may change annually. You authorize us to charge or "
-"deduct applicable fees owed in connection with deposits, exchanges and "
-"withdrawals following the rules of the Taler protocol. We reserve the "
-"right to provide different types of rewards to users either in the form "
-"of discount for our Services or in any other form at our discretion and "
-"without prior notice to you."
-msgstr ""
-
-#: ../../tos.rst:108
-msgid "Eligibility"
-msgstr ""
-
-#: ../../tos.rst:110
-msgid ""
-"To be eligible to use our Services, you must be able to form legally "
-"binding contracts or have the permission of your legal guardian. By using"
-" our Services, you represent and warrant that you meet all eligibility "
-"requirements that we outline in these Terms."
+#: ../../tos.rst:90
+msgid "When using our Services, you agree to not take any action that intentionally imposes an unreasonable load on our infrastructure. If you find security problems in our Services, you agree to first report them to security@taler-systems.com and grant us the right to publish your report. We warrant that we will ourselves publicly disclose any issues reported within 3 months, and that we will not prosecute anyone reporting security issues if they did not exploit the issue beyond a proof-of-concept, and followed the above responsible disclosure practice."
+msgstr ""
+
+#: ../../tos.rst:101
+msgid "Fees"
msgstr ""
-#: ../../tos.rst:116
-msgid "Financial self-responsibility"
+#: ../../tos.rst:103
+msgid "You agree to pay the fees for exchanges and withdrawals completed via the Taler Wallet (\"Fees\") as defined by us, which we may change from time to time. With the exception of wire transfer fees, Taler transaction fees are set for any electronic coin at the time of withdrawal and fixed throughout the validity period of the respective electronic coin. Your wallet should obtain and display applicable fees when withdrawing funds. Fees for coins obtained as change may differ from the fees applicable to the original coin. Wire transfer fees that are independent from electronic coins may change annually. You authorize us to charge or deduct applicable fees owed in connection with deposits, exchanges and withdrawals following the rules of the Taler protocol. We reserve the right to provide different types of rewards to users either in the form of discount for our Services or in any other form at our discretion and without prior notice to you."
msgstr ""
#: ../../tos.rst:118
-msgid ""
-"You will be responsible for maintaining the availability, integrity and "
-"confidentiality of the data stored in your wallet. When you setup a Taler"
-" Wallet, you are strongly advised to follow the precautionary measures "
-"offered by the software to minimize the chances to losse access to or "
-"control over your Wallet data. We will not be liable for any loss or "
-"damage arising from your failure to comply with this paragraph."
+msgid "Eligibility and Financial self-responsibility"
msgstr ""
-#: ../../tos.rst:126
-msgid "Copyrights and trademarks"
+#: ../../tos.rst:120
+msgid "To be eligible to use our Services, you must be able to form legally binding contracts or have the permission of your legal guardian. By using our Services, you represent and warrant that you meet all eligibility requirements that we outline in these Terms."
msgstr ""
-#: ../../tos.rst:128
-msgid ""
-"The Taler Wallet is released under the terms of the GNU General Public "
-"License (GNU GPL). You have the right to access, use, and share the Taler"
-" Wallet, in modified or unmodified form. However, the GPL is a strong "
-"copyleft license, which means that any derivative works must be "
-"distributed under the same license terms as the original software. If you"
-" have any questions, you should review the GNU GPL’s full terms and "
-"conditions at https://www.gnu.org/licenses/gpl-3.0.en.html. “Taler” "
-"itself is a trademark of Taler Systems SA. You are welcome to use the "
-"name in relation to processing payments using the Taler protocol, "
-"assuming your use is compatible with an official release from the GNU "
-"Project that is not older than two years."
+#: ../../tos.rst:125
+msgid "You will be responsible for maintaining the availability, integrity and confidentiality of the data stored in your wallet. When you setup a Taler Wallet, you are strongly advised to follow the precautionary measures offered by the software to minimize the chances to losse access to or control over your Wallet data. We will not be liable for any loss or damage arising from your failure to comply with this paragraph."
msgstr ""
-#: ../../tos.rst:140
-msgid "Your use of our services"
+#: ../../tos.rst:133
+msgid "Copyrights and trademarks"
msgstr ""
-#: ../../tos.rst:142
-msgid ""
-"When using our Services, you agree to not take any action that "
-"intentionally imposes an unreasonable load on our infrastructure. If you "
-"find security problems in our Services, you agree to first report them to"
-" security@taler-systems.com and grant us the right to publish your "
-"report. We warrant that we will ourselves publicly disclose any issues "
-"reported within 3 months, and that we will not prosecute anyone reporting"
-" security issues if they did not exploit the issue beyond a proof-of-"
-"concept, and followed the above responsible disclosure practice."
+#: ../../tos.rst:135
+msgid "The Taler Wallet is released under the terms of the GNU General Public License (GNU GPL). You have the right to access, use, and share the Taler Wallet, in modified or unmodified form. However, the GPL is a strong copyleft license, which means that any derivative works must be distributed under the same license terms as the original software. If you have any questions, you should review the GNU GPL’s full terms and conditions at https://www.gnu.org/licenses/gpl-3.0.en.html. “Taler” itself is a trademark of Taler Systems SA. You are welcome to use the name in relation to processing payments using the Taler protocol, assuming your use is compatible with an official release from the GNU Project that is not older than two years."
msgstr ""
-#: ../../tos.rst:152
+#: ../../tos.rst:148
msgid "Limitation of liability & disclaimer of warranties"
msgstr ""
-#: ../../tos.rst:154
-msgid ""
-"You understand and agree that we have no control over, and no duty to "
-"take any action regarding: Failures, disruptions, errors, or delays in "
-"processing that you may experience while using our Services; The risk of "
-"failure of hardware, software, and Internet connections; The risk of "
-"malicious software being introduced or found in the software underlying "
-"the Taler Wallet; The risk that third parties may obtain unauthorized "
-"access to information stored within your Taler Wallet, including, but not"
-" limited to your Taler Wallet coins or backup encryption keys. You "
-"release us from all liability related to any losses, damages, or claims "
-"arising from:"
+#: ../../tos.rst:150
+msgid "You understand and agree that we have no control over, and no duty to take any action regarding: Failures, disruptions, errors, or delays in processing that you may experience while using our Services; The risk of failure of hardware, software, and Internet connections; The risk of malicious software being introduced or found in the software underlying the Taler Wallet; The risk that third parties may obtain unauthorized access to information stored within your Taler Wallet, including, but not limited to your Taler Wallet coins or backup encryption keys. You release us from all liability related to any losses, damages, or claims arising from:"
msgstr ""
-#: ../../tos.rst:164
-msgid ""
-"user error such as forgotten passwords, incorrectly constructed "
-"transactions;"
+#: ../../tos.rst:160
+msgid "user error such as forgotten passwords, incorrectly constructed transactions;"
msgstr ""
-#: ../../tos.rst:166
+#: ../../tos.rst:162
msgid "server failure or data loss;"
msgstr ""
-#: ../../tos.rst:167
+#: ../../tos.rst:163
msgid "unauthorized access to the Taler Wallet application;"
msgstr ""
-#: ../../tos.rst:168
+#: ../../tos.rst:164
msgid "bugs or other errors in the Taler Wallet software; and"
msgstr ""
-#: ../../tos.rst:169
-msgid ""
-"any unauthorized third party activities, including, but not limited to, "
-"the use of viruses, phishing, brute forcing, or other means of attack "
-"against the Taler Wallet. We make no representations concerning any Third"
-" Party Content contained in or accessed through our Services."
+#: ../../tos.rst:165
+msgid "any unauthorized third party activities, including, but not limited to, the use of viruses, phishing, brute forcing, or other means of attack against the Taler Wallet. We make no representations concerning any Third Party Content contained in or accessed through our Services."
msgstr ""
-#: ../../tos.rst:174
-msgid ""
-"Any other terms, conditions, warranties, or representations associated "
-"with such content, are solely between you and such organizations and/or "
-"individuals."
+#: ../../tos.rst:170
+msgid "Any other terms, conditions, warranties, or representations associated with such content, are solely between you and such organizations and/or individuals."
msgstr ""
-#: ../../tos.rst:179
-msgid "Limitation of liability"
+#: ../../tos.rst:174
+msgid "To the fullest extent permitted by applicable law, in no event will we or any of our officers, directors, representatives, agents, servants, counsel, employees, consultants, lawyers, and other personnel authorized to act, acting, or purporting to act on our behalf (collectively the “Taler Parties”) be liable to you under contract, tort, strict liability, negligence, or any other legal or equitable theory, for:"
msgstr ""
#: ../../tos.rst:181
-msgid ""
-"To the fullest extent permitted by applicable law, in no event will we or"
-" any of our officers, directors, representatives, agents, servants, "
-"counsel, employees, consultants, lawyers, and other personnel authorized "
-"to act, acting, or purporting to act on our behalf (collectively the "
-"“Taler Parties”) be liable to you under contract, tort, strict liability,"
-" negligence, or any other legal or equitable theory, for:"
+msgid "any lost profits, data loss, cost of procurement of substitute goods or services, or direct, indirect, incidental, special, punitive, compensatory, or consequential damages of any kind whatsoever resulting from:"
msgstr ""
-#: ../../tos.rst:188
-msgid ""
-"any lost profits, data loss, cost of procurement of substitute goods or "
-"services, or direct, indirect, incidental, special, punitive, "
-"compensatory, or consequential damages of any kind whatsoever resulting "
-"from:"
-msgstr ""
-
-#: ../../tos.rst:192
+#: ../../tos.rst:185
msgid "your use of, or conduct in connection with, our services;"
msgstr ""
-#: ../../tos.rst:193
-msgid ""
-"any unauthorized use of your wallet and/or private key due to your "
-"failure to maintain the confidentiality of your wallet;"
+#: ../../tos.rst:186
+msgid "any unauthorized use of your wallet and/or private key due to your failure to maintain the confidentiality of your wallet;"
msgstr ""
-#: ../../tos.rst:195
+#: ../../tos.rst:188
msgid "any interruption or cessation of transmission to or from the services; or"
msgstr ""
-#: ../../tos.rst:196
-msgid ""
-"any bugs, viruses, trojan horses, or the like that are found in the Taler"
-" Wallet software or that may be transmitted to or through our services by"
-" any third party (regardless of the source of origination), or"
+#: ../../tos.rst:189
+msgid "any bugs, viruses, trojan horses, or the like that are found in the Taler Wallet software or that may be transmitted to or through our services by any third party (regardless of the source of origination), or"
msgstr ""
-#: ../../tos.rst:200
+#: ../../tos.rst:193
msgid "any direct damages."
msgstr ""
-#: ../../tos.rst:202
-msgid ""
-"These limitations apply regardless of legal theory, whether based on "
-"tort, strict liability, breach of contract, breach of warranty, or any "
-"other legal theory, and whether or not we were advised of the possibility"
-" of such damages. Some jurisdictions do not allow the exclusion or "
-"limitation of liability for consequential or incidental damages, so the "
-"above limitation may not apply to you."
-msgstr ""
-
-#: ../../tos.rst:210
-msgid "Warranty disclaimer"
+#: ../../tos.rst:195
+msgid "These limitations apply regardless of legal theory, whether based on tort, strict liability, breach of contract, breach of warranty, or any other legal theory, and whether or not we were advised of the possibility of such damages. Some jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, so the above limitation may not apply to you."
msgstr ""
-#: ../../tos.rst:212
-msgid ""
-"Our services are provided \"as is\" and without warranty of any kind. To "
-"the maximum extent permitted by law, we disclaim all representations and "
-"warranties, express or implied, relating to the services and underlying "
-"software or any content on the services, whether provided or owned by us "
-"or by any third party, including without limitation, warranties of "
-"merchantability, fitness for a particular purpose, title, non-"
-"infringement, freedom from computer virus, and any implied warranties "
-"arising from course of dealing, course of performance, or usage in trade,"
-" all of which are expressly disclaimed. In addition, we do not represent "
-"or warrant that the content accessible via the services is accurate, "
-"complete, available, current, free of viruses or other harmful "
-"components, or that the results of using the services will meet your "
-"requirements. Some states do not allow the disclaimer of implied "
-"warranties, so the foregoing disclaimers may not apply to you. This "
-"paragraph gives you specific legal rights and you may also have other "
-"legal rights that vary from state to state."
+#: ../../tos.rst:202
+msgid "Our services are provided \"as is\" and without warranty of any kind. To the maximum extent permitted by law, we disclaim all representations and warranties, express or implied, relating to the services and underlying software or any content on the services, whether provided or owned by us or by any third party, including without limitation, warranties of merchantability, fitness for a particular purpose, title, non-infringement, freedom from computer virus, and any implied warranties arising from course of dealing, course of performance, or usage in trade, all of which are expressly disclaimed. In addition, we do not represent or warrant that the content accessible via the services is accurate, complete, available, current, free of viruses or other harmful components, or that the results of using the services will meet your requirements. Some states do not allow the disclaimer of implied warranties, so the foregoing disclaimers may not apply to you. This paragraph gives you specific legal rights and you may also have other legal rights that vary from state to state."
msgstr ""
-#: ../../tos.rst:229
-msgid "Indemnity"
+#: ../../tos.rst:219
+msgid "Indemnity and Time limitation on claims and Termination"
msgstr ""
-#: ../../tos.rst:231
-msgid ""
-"To the extent permitted by applicable law, you agree to defend, "
-"indemnify, and hold harmless the Taler Parties from and against any and "
-"all claims, damages, obligations, losses, liabilities, costs or debt, and"
-" expenses (including, but not limited to, attorney’s fees) arising from: "
-"(a) your use of and access to the Services; (b) any feedback or "
-"submissions you provide to us concerning the Taler Wallet; (c) your "
-"violation of any term of this Agreement; or (d) your violation of any "
-"law, rule, or regulation, or the rights of any third party."
+#: ../../tos.rst:221
+msgid "To the extent permitted by applicable law, you agree to defend, indemnify, and hold harmless the Taler Parties from and against any and all claims, damages, obligations, losses, liabilities, costs or debt, and expenses (including, but not limited to, attorney’s fees) arising from: (a) your use of and access to the Services; (b) any feedback or submissions you provide to us concerning the Taler Wallet; (c) your violation of any term of this Agreement; or (d) your violation of any law, rule, or regulation, or the rights of any third party."
msgstr ""
-#: ../../tos.rst:240
-msgid "Time limitation on claims"
+#: ../../tos.rst:229
+msgid "You agree that any claim you may have arising out of or related to your relationship with us must be filed within one year after such claim arises, otherwise, your claim in permanently barred."
msgstr ""
-#: ../../tos.rst:242
-msgid ""
-"You agree that any claim you may have arising out of or related to your "
-"relationship with us must be filed within one year after such claim "
-"arises, otherwise, your claim in permanently barred."
+#: ../../tos.rst:233
+msgid "In the event of termination concerning your use of our Services, your obligations under this Agreement will still continue."
msgstr ""
-#: ../../tos.rst:247
-msgid "Governing law"
+#: ../../tos.rst:238
+msgid "Discontinuance of services and Force majeure"
msgstr ""
-#: ../../tos.rst:249
-msgid ""
-"No matter where you’re located, the laws of Switzerland will govern these"
-" Terms. If any provisions of these Terms are inconsistent with any "
-"applicable law, those provisions will be superseded or modified only to "
-"the extent such provisions are inconsistent. The parties agree to submit "
-"to the ordinary courts in Zurich, Switzerland for exclusive jurisdiction "
-"of any dispute arising out of or related to your use of the Services or "
-"your breach of these Terms."
+#: ../../tos.rst:240
+msgid "We may, in our sole discretion and without cost to you, with or without prior notice, and at any time, modify or discontinue, temporarily or permanently, any portion of our Services. We will use the Taler protocol’s provisions to notify Wallets if our Services are to be discontinued. It is your responsibility to ensure that the Taler Wallet is online at least once every three months to observe these notifications. We shall not be held responsible or liable for any loss of funds in the event that we discontinue or depreciate the Services and your Taler Wallet fails to transfer out the coins within a three months notification period."
msgstr ""
-#: ../../tos.rst:258
-msgid "Termination"
+#: ../../tos.rst:250
+msgid "We shall not be held liable for any delays, failure in performance, or interruptions of service which result directly or indirectly from any cause or condition beyond our reasonable control, including but not limited to: any delay or failure due to any act of God, act of civil or military authorities, act of terrorism, civil disturbance, war, strike or other labor dispute, fire, interruption in telecommunications or Internet services or network provider services, failure of equipment and/or software, other catastrophe, or any other occurrence which is beyond our reasonable control and shall not affect the validity and enforceability of any remaining provisions."
msgstr ""
-#: ../../tos.rst:260
-msgid ""
-"In the event of termination concerning your use of our Services, your "
-"obligations under this Agreement will still continue."
+#: ../../tos.rst:262
+msgid "Governing law, Waivers, Severability and Assignment"
msgstr ""
#: ../../tos.rst:264
-msgid "Discontinuance of services"
+msgid "No matter where you’re located, the laws of Switzerland will govern these Terms. If any provisions of these Terms are inconsistent with any applicable law, those provisions will be superseded or modified only to the extent such provisions are inconsistent. The parties agree to submit to the ordinary courts in Zurich, Switzerland for exclusive jurisdiction of any dispute arising out of or related to your use of the Services or your breach of these Terms."
msgstr ""
-#: ../../tos.rst:266
-msgid ""
-"We may, in our sole discretion and without cost to you, with or without "
-"prior notice, and at any time, modify or discontinue, temporarily or "
-"permanently, any portion of our Services. We will use the Taler "
-"protocol’s provisions to notify Wallets if our Services are to be "
-"discontinued. It is your responsibility to ensure that the Taler Wallet "
-"is online at least once every three months to observe these "
-"notifications. We shall not be held responsible or liable for any loss of"
-" funds in the event that we discontinue or depreciate the Services and "
-"your Taler Wallet fails to transfer out the coins within a three months "
-"notification period."
+#: ../../tos.rst:272
+msgid "Our failure to exercise or delay in exercising any right, power, or privilege under this Agreement shall not operate as a waiver; nor shall any single or partial exercise of any right, power, or privilege preclude any other or further exercise thereof."
msgstr ""
#: ../../tos.rst:277
-msgid "No waiver"
+msgid "You agree that we may assign any of our rights and/or transfer, sub-contract, or delegate any of our obligations under these Terms."
msgstr ""
-#: ../../tos.rst:279
-msgid ""
-"Our failure to exercise or delay in exercising any right, power, or "
-"privilege under this Agreement shall not operate as a waiver; nor shall "
-"any single or partial exercise of any right, power, or privilege preclude"
-" any other or further exercise thereof."
+#: ../../tos.rst:280
+msgid "If it turns out that any part of this Agreement is invalid, void, or for any reason unenforceable, that term will be deemed severable and limited or eliminated to the minimum extent necessary."
msgstr ""
-#: ../../tos.rst:285
-msgid "Severability"
+#: ../../tos.rst:284
+msgid "This Agreement sets forth the entire understanding and agreement as to the subject matter hereof and supersedes any and all prior discussions, agreements, and understandings of any kind (including, without limitation, any prior versions of this Agreement) and every nature between us. Except as provided for above, any modification to this Agreement must be in writing and must be signed by both parties."
msgstr ""
-#: ../../tos.rst:287
-msgid ""
-"If it turns out that any part of this Agreement is invalid, void, or for "
-"any reason unenforceable, that term will be deemed severable and limited "
-"or eliminated to the minimum extent necessary."
-msgstr ""
-
-#: ../../tos.rst:292
-msgid "Force majeure"
-msgstr ""
-
-#: ../../tos.rst:294
-msgid ""
-"We shall not be held liable for any delays, failure in performance, or "
-"interruptions of service which result directly or indirectly from any "
-"cause or condition beyond our reasonable control, including but not "
-"limited to: any delay or failure due to any act of God, act of civil or "
-"military authorities, act of terrorism, civil disturbance, war, strike or"
-" other labor dispute, fire, interruption in telecommunications or "
-"Internet services or network provider services, failure of equipment "
-"and/or software, other catastrophe, or any other occurrence which is "
-"beyond our reasonable control and shall not affect the validity and "
-"enforceability of any remaining provisions."
-msgstr ""
-
-#: ../../tos.rst:305
-msgid "Assignment"
-msgstr ""
-
-#: ../../tos.rst:307
-msgid ""
-"You agree that we may assign any of our rights and/or transfer, sub-"
-"contract, or delegate any of our obligations under these Terms."
-msgstr ""
-
-#: ../../tos.rst:311
-msgid "Entire agreement"
-msgstr ""
-
-#: ../../tos.rst:313
-msgid ""
-"This Agreement sets forth the entire understanding and agreement as to "
-"the subject matter hereof and supersedes any and all prior discussions, "
-"agreements, and understandings of any kind (including, without "
-"limitation, any prior versions of this Agreement) and every nature "
-"between us. Except as provided for above, any modification to this "
-"Agreement must be in writing and must be signed by both parties."
-msgstr ""
-
-#: ../../tos.rst:321
+#: ../../tos.rst:293
msgid "Questions or comments"
msgstr ""
-#: ../../tos.rst:323
-msgid ""
-"We welcome comments, questions, concerns, or suggestions. Please send us "
-"a message on our contact page at legal@taler-systems.com."
+#: ../../tos.rst:295
+msgid "We welcome comments, questions, concerns, or suggestions. Please send us a message on our contact page at legal@taler-systems.com."
msgstr ""
-
-#~ msgid "Indemntiy"
-#~ msgstr ""
-
diff --git a/contrib/tos/tos.rst b/contrib/tos/tos.rst
index 9e500cae..2509b87b 100644
--- a/contrib/tos/tos.rst
+++ b/contrib/tos/tos.rst
@@ -259,7 +259,7 @@ the validity and enforceability of any remaining provisions.
Governing law, Waivers, Severability and Assignment
-------------------------------------------------
+---------------------------------------------------
No matter where you’re located, the laws of Switzerland will govern these
Terms. If any provisions of these Terms are inconsistent with any applicable
diff --git a/debian/.gitignore b/debian/.gitignore
index 46b94f7a..f3ddfd1d 100644
--- a/debian/.gitignore
+++ b/debian/.gitignore
@@ -18,3 +18,6 @@ taler-auditor.postrm.debhelper
taler-auditor.substvars
taler-auditor/
taler-exchange.postrm.debhelper
+*.debhelper
+*.substvars
+taler-exchange-offline
diff --git a/debian/changelog b/debian/changelog
index c6d809bd..8dcaf12d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+taler-exchange (0.8.5-3) unstable; urgency=low
+
+ * Updating to latest Git with minor bugfixes and improvements.
+
+ -- Christian Grothoff <grothoff@taler.net> Tue, 12 Oct 2021 13:12:58 +0200
+
+taler-exchange (0.8.5-2) unstable; urgency=low
+
+ * Updating to latest Git with minor bugfixes and improvements.
+
+ -- Christian Grothoff <grothoff@taler.net> Mon, 27 Sep 2021 13:12:58 +0200
+
taler-exchange (0.8.5-1) unstable; urgency=low
* Updating to latest Git with minor bugfixes and improvements.
diff --git a/doc/cbdc-es/cbdc-es.tex b/doc/cbdc-es/cbdc-es.tex
new file mode 100644
index 00000000..8c87c3e0
--- /dev/null
+++ b/doc/cbdc-es/cbdc-es.tex
@@ -0,0 +1,1283 @@
+\documentclass[a4paper,10pt]{article} %tamaño de papel y letra ``base''
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage[top=2cm,
+bottom=2cm,
+includefoot,
+left=3cm,
+right=2cm,
+footskip=1cm]{geometry}
+\usepackage{url}
+\IfFileExists{lmodern.sty}{\usepackage{lmodern}}{}
+\usepackage{graphicx}
+\usepackage{mathpazo}
+\usepackage{amsmath}
+\usepackage{mathptmx}
+\usepackage{color}
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage[hidelinks]{hyperref}
+ %\usepackage{natbib,har2nat}
+\usepackage{natbib}
+\usepackage[spanish]{babel}
+\input{eshyphexh.tex}
+ %\renewcommand{\abstractname}{Resumen}
+ %\renewcommand{\refname}{Referencias}
+ % de estas cosas se ocupa \usepackage[spanish]{babel}
+
+
+\title{Cómo Emitir una Moneda Digital del Banco Central}
+\author{David Chaum\footnote{david@chaum.com} \\
+ xx Network \and
+ Christian Grothoff\footnote{christian.grothoff@bfh.ch} \\
+ BFH\footnote{Universidad de Ciencias Aplicadas de Berna}
+ \quad y Proyecto GNU \and
+ Thomas Moser\footnote{thomas.moser@snb.ch}\\
+ Banco Nacional de Suiza}
+\date{Esta versión: octubre 2021 \\
+ Primera versión: mayo 2020}
+
+
+
+\begin{document}
+
+\maketitle
+
+\begin{abstract}
+Con la aparición de Bitcoin y monedas estables propuestas recientemente
+por grandes empresas tecnológicas como Diem (antes Libra), los bancos
+centrales se enfrentan a la creciente competencia de particulares que
+ofrecen su propia alternativa digital al dinero en efectivo. No
+abordamos la cuestión normativa de si un un banco central debería o no
+emitir una moneda digital del banco central (Central Bank Digital
+Currency -- CBDC). Contribuimos en cambio al actual debate de
+investigación mostrando de qué manera un banco central podría hacerlo si
+así lo deseara. Proponemos un sistema basado en tokens sin tecnología de
+libro mayor distribuido, y mostramos que el efectivo electrónico ya
+implementado solo mediante software se puede mejorar para preservar la
+privacidad en las transacciones, cumplir con los requisitos
+reglamentarios de modo convincente y ofrecer un nivel de protección de
+resistencia cuántica contra los riesgos sistémicos que amenazan la
+privacidad. Ni la política monetaria ni la estabilidad financiera se
+verían materialmente afectadas porque una CBDC con este diseño
+replicaría el efectivo físico en lugar de los depósitos bancarios. \\
+JEL: E42, E51, E52, E58, G2
+\\
+Keywords: Monedas digitales, banco central, CBDC, firmas ciegas, monedas
+estables
+\end{abstract}
+
+\vspace{40pt}
+
+\section*{Agradecimientos}
+Agradecemos a Michael Barczay, Roman Baumann, Morten Bech, Nicolas Cuche,
+Florian Dold, Andreas Fuster, Stefan Kügel, Benjamin Müller, Dirk Niepelt,
+Oliver Sigrist, Richard Stallman, Andreas Wehrli, y tres colaboradores
+anónimos por sus comentarios y sugerencias. Las ideas, opiniones,
+investigaciones y conclusiones o recomendaciones expresadas en este
+documento pertenecen estrictamente a los autores. No reflejan
+necesariamente los puntos de vista del Banco Nacional de Suiza (BNS). El
+BNS no asume ninguna responsabilidad por errores u omisiones ni por la
+exactitud de la información contenida en este documento.
+
+Traducción: Javier Sepulveda \& Dora Scilipoti
+
+
+\newpage
+
+%\tableofcontents
+
+\section{Introducción}\label{1.-introducciuxf3n}
+
+Desde la aparición de los ordenadores personales en los años ochenta, y
+especialmente desde que en 1991 la National Science Foundation quitara
+las restricciones al uso de Internet para propósitos comerciales, se ha
+buscado crear dinero digital para realizar pagos en línea. La primera
+propuesta la realizó~\citet{Chaum1983}. A pesar de que tales métodos fueron
+implementados, no prosperaron. Fueron en cambio los sistemas con tarjeta
+de crédito los que se convirtieron en el método dominante para pagos en
+línea. La propuesta de~\citet{Nakamoto} para un sistema P2P de dinero
+digital y el posterior lanzamiento exitoso de Bitcoin desataron una
+nueva era de investigación sobre el tema y desarrollo de dinero digital.
+CoinMarketCap enumera más de 5.000 criptomonedas. Recientemente los
+bancos centrales han empezado a considerar, o al menos estudiar, la
+emisión de monedas digitales~\cite[véase][]{AuerBoehme,AuerCornelli,Boar,Kiff,Mancini-Griffoli}.
+
+Actualmente los bancos centrales emiten dos tipos de dinero: (i)
+reservas en forma de cuentas de liquidación en los bancos centrales para
+determinados participantes del mercado financiero y (ii) moneda en forma
+de billetes disponibles para el público. En consecuencia, la
+bibliografía sobre la moneda digital del banco central (CBDC) distingue
+entre (a) venta de CBDC al por mayor, con acceso limitado, y (b) venta
+de CBDC al por menor, accesible al público \cite[véase, p. ej.][]{Bech}.
+Una CBDC al por mayor sería menos disruptiva para el sistema
+actual debido a que los bancos y los participantes seleccionados del
+mercado financiero ya tienen acceso a dinero digital del banco en forma
+de cuentas del banco central, que utilizan para liquidar pagos
+interbancarios. La cuestión aquí es si la tokenización del dinero de un
+banco central y la tecnología de libro mayor distribuido (Distributed
+Ledger Technology - DLT) ofrecen beneficios netos en comparación con los
+sistemas de liquidación bruta en tiempo real (Real-Time Gross
+Settlement - RTGS). Hasta el momento, la conclusión es que no es así, al
+menos cuando se trata de pagos interbancarios nacionales~\cite[véase][]{Chapman}.
+
+Una CBDC al por menor, que sería una nueva forma de dinero del banco
+central a disposición del público, podría ser más disruptiva para el
+sistema actual, dependiendo de su diseño. Cuanto más compita una CBDC de
+este tipo con los depósitos bancarios comerciales, mayor será la amenaza
+para la financiación bancaria, con un posible impacto adverso en el
+crédito bancario y la actividad económica~\cite[véase][]{Agur}. Sin
+embargo, una CBDC al por menor podría también tener
+beneficios~\cite[véase][]{Bordo,Berentsen,Bindseil,Niepelt,Riksbank,BoE}.
+Poner a disposición de
+todos dinero electrónico del banco central sin riesgo de contrapartida
+podría mejorar la estabilidad y la resistencia del sistema de pago al
+por menor. También podría proporcionar una infraestructura de pago
+neutral para promover la competencia, la eficiencia y la innovación. En
+general, es probable que los costos y beneficios de una CBDC al por
+menor difieran de un país a otro. Para conocer la opinión del Banco
+Nacional de Suiza, que no tiene planes de emitir una CBDC al por
+menor~\cite[véase][]{Jordan}.
+
+El presente documento se centra en una CBDC al por menor, pero no abordamos la
+cuestión de si un banco central \emph{debería o no} emitir una moneda
+CBDC. Nos centramos en cambio en el diseño potencial de una
+CBCD. Recientemente ha habido un creciente interés en el diseño de monedas
+CBCD (\cite[véase p. ej.][]{Allen,BoE}). El diseño que proponemos difiere
+significativamente de otras propuestas. Nuestro sistema se basa en la
+tecnología eCash descrita por Chaum~\cite{Chaum1983,Chaum1990},
+mejorándola. En particular, proponemos un sistema para CBCD basado en tokens y
+solo mediante software, sin blockchain para la DLT. La DLT es un diseño
+interesante en ausencia de un actor principal o si las entidades que
+interactúan no concuerdan en nombrar un actor central de confianza. Sin
+embargo, este no es el caso de una CBCD al por menor emitida por un
+\emph{banco central}. Distribuir el libro mayor del banco central con una
+blockchain solo aumenta los costes de transacción, no proporciona beneficios
+tangibles en una implementación por parte de un banco central. Utilizar la DLT
+para emitir dinero digital puede ser útil si no hay un banco central para
+empezar (p. ej. el proyecto Sovereign de las Islas Marshall) o si la
+intención explícita es prescindir de un banco central
+(p. ej. Bitcoin).\footnote{Puede haber buenos casos de uso para la DLT en el
+caso de infraestructura de mercado financiero, tal como los intercambios
+digitales, donde surge la cuestión de como obtener dinero del banco central en
+la DLT a efectos de liquidación. Sin embargo en esas situaciones, los
+beneficios potenciales de la DLT, por ejemplo menos costes o reconciliación
+automática, no surgen de una emisión descentralizada del dinero del banco
+central.}
+
+La CBCD basada en tokens que se propone aquí permite también la
+preservación de una cualidad clave del dinero físico: la privacidad en
+la transacción. Usualmente se argumenta que las protecciones
+criptográficas para la privacidad exigen tantos recursos computacionales
+que su utilización en dispositivos móviles no es factible~\cite[véase][]{Allen}.
+Si bien esto puede ser cierto en el contexto de la DLT,
+donde la rastreabilidad pública de las transacciones es necesaria para
+prevenir el doble gasto~\cite{Narayanan}, no es cierto para el
+protocolo de firma ciega de tipo Chaum con un banco central que se
+propone en el presente documento. Nuestra CBDC, basada en firmas ciegas
+y arquitectura de dos niveles, garantiza una perfecta privacidad de
+resistencia cuántica en las transacciones, al mismo tiempo que
+proporciona protecciones sociales tales como impedir el lavado de dinero
+(Anti-Money Laundering - AML) y financiar la lucha contra el terrorismo
+(Counter Terrorism Financing -- CFT), protecciones que de hecho tienen
+mayor fuerza que con los billetes.
+
+La privacidad en las transacciones es importante por tres razones.
+Primero, porque protege a los usuarios frente al escrutinio y el abuso
+de vigilancia gubernamental. Los programas de vigilancia masiva son
+problemáticos incluso si las personas creen que no tienen nada que
+esconder, simplemente por la posibilidad de error y abuso,
+particularmente si los programas carecen de transparencia e
+imputabilidad~\cite[véase][]{Solove}. Segundo, porque la privacidad en las
+transacciones protege a los usuarios frente a la explotación de datos por parte
+de los proveedores de servicios de pago.
+Tercero, porque protege a los usuarios frente a la contraparte en la
+transacción, descartando la posibilidad de un posterior comportamiento
+oportunista, o frente a riesgos de seguridad debido a fallos o
+negligencia en la protección de los datos del cliente~\cite[véase][]{Kahn2005}.
+
+Este documento está estructurado como sigue: en la sección 2 explicamos
+la diferencia entre el dinero del banco central y otro dinero. En la
+sección 3 analizamos los diseños de CBDC comunes y simplistas, antes
+de proponer nuestro diseño en la sección 4. Luego comentamos
+consideraciones políticas y normativas (5) y trabajos relacionados (6);
+en fin, concluimos (7).
+
+
+\section{¿Qué es el dinero del banco central?}
+ \label{2.-quuxe9-es-el-dinero-del-banco-central}
+
+El dinero es un activo que puede ser usado para comprar bienes y
+servicios. Para ser considerado dinero, este activo debe ser aceptado
+por otras entidades distintas del emisor. Este es el motivo por el que
+los vales, por ejemplo, no se consideran dinero. El dinero genuino tiene
+que ser aceptado \emph{comúnmente} como medio de intercambio. Si bien el
+dinero tiene otras funciones, por ejemplo como unidad de cuenta y
+depósito de valor, la característica que lo distingue es su función como
+medio de intercambio. Normalmente, la unidad de cuenta (p. ej. cómo se
+cotizan los precios y cómo se registran las deudas) coincide con el
+medio de intercambio por razones de conveniencia. La separación puede
+ocurrir, sin embargo, si el valor del medio de intercambio carece de
+estabilidad en relación a los bienes y servicios
+comercializados.\footnote{Esto puede ocurrir espontáneamente en un entorno
+de alta-inflación, p. ej. cuando los precios se fijan en USD pero los pagos
+se realizan en divisa local. Lo mismo es cierto para los pagos en Bitcoin,
+donde los precios usualmente se fijan en USA u otras divisas locales debido a
+la alta volatilidad de Bitcoin. Una separación también puede ocurrir por el
+diseño, p. ej. en la Unidad de Fomento (UF) de Chile o la Special Drawing Right
+(SDR) del fondo monetario internacional (IMF). Sin embargo, también entonces el
+propósito es tener una unidad de cuenta más estable.} El dinero debe también ser
+un depósito de valor para poder actuar como medio de intercambio, porque
+debe preservar su poder de compra desde el momento en que se recibe
+hasta el momento en que se gasta. Sin embargo, varios otros activos
+sirven como depósito de valor, como por ejemplo acciones, bonos, metales
+preciosos e inmuebles. Por tanto, la característica como depósito de
+valor no es distintiva del dinero.
+
+En la economía moderna, el público usa dos tipos diferentes de dinero:
+(a) dinero estatal y (b) dinero privado. El dinero estatal lo emite
+típicamente un banco central, que actúa como agente del Estado. El
+dinero del banco central está disponible para determinadas instituciones
+financieras en forma de depósitos en el banco central (reservas) y para
+el público en forma de moneda (billetes y monedas), también llamado
+``efectivo''. En una economía moderna con dinero fiduciario, tal dinero
+no tiene valor intrínseco. Legalmente es una obligación del banco
+central, aunque no es canjeable.
+
+En la mayoría de los países, el dinero del banco central se define como
+moneda de curso legal, lo cual significa que debe ser aceptado como pago
+de una deuda monetaria, incluyendo impuestos y multas legales. Si bien
+esto garantiza que el dinero del banco central tenga algún valor, el
+estatus de moneda de curso legal es insuficiente para que el dinero del
+banco central mantenga un valor estable. Más bien, es la política
+monetaria de los bancos centrales la que mantiene el valor del dinero.
+Mantener la estabilidad de los precios, es decir, un valor estable del
+dinero en relación con el valor de los bienes y servicios
+comercializados, es una de las principales responsabilidades de los
+bancos centrales.
+
+En una economía moderna, la mayoría de los pagos se hacen con dinero
+privado emitido por bancos comerciales. Tal dinero se compone de
+depósitos a la vista que la gente tiene en los bancos comerciales. A
+estos depósitos bancarios se puede acceder con cheques, tarjetas de
+débito, tarjetas de crédito, u otros medios para transferir dinero. Son
+una obligación del respectivo banco comercial. Una característica
+fundamental de los depósitos bancarios es que los bancos comerciales
+garantizan la convertibilidad, bajo demanda, en dinero del banco central
+a un precio fijo, es decir, a la par. Los depositantes pueden retirar
+sus fondos en efectivo o transferirlos a una tasa fija de 1:1. Los
+bancos comerciales mantienen estable el valor de su dinero vinculándolo
+al dinero del banco central.
+
+No obstante, en un sistema de reserva fraccionado, un banco comercial
+-- incluso siendo solvente -- puede no contar con la liquidez necesaria
+para cumplir su promesa de convertir los depósitos bancarios en dinero
+del banco central (p. ej. en caso de una caída bancaria) de manera tal
+que los clientes no puedan retirar su dinero. Un banco también puede
+llegar a ser insolvente e ir a la bancarrota, y como resultado los
+clientes pueden perder su dinero. Así, los bancos comerciales están
+regulados para mitigar estos riesgos.
+
+Una diferencia significativa entre el dinero de un banco central y el
+dinero emitido privadamente por un banco comercial es, por lo tanto, que
+este último conlleva un riesgo para la contraparte. Un banco central
+puede siempre cumplir con sus obligaciones usando su propio dinero no
+reembolsable. El dinero del banco central es el único activo monetario
+de una economía nacional sin riesgo crediticio o de liquidez. Por lo
+tanto, es el activo que típicamente se prefiere para los pagos en las
+infraestructuras del mercado financiero (véase p. ej. CPMI-IOSCO
+\emph{Principles for Financial Market Infrastructures}, 2012). Otra
+diferencia es que el dinero del banco central afianza el sistema
+monetario nacional al proporcionar una referencia de valor con la que el
+dinero de los bancos comerciales mantiene una convertibilidad a la par.
+
+Aparte de los bancos comerciales, otra entidades privadas ocasionalmente
+intentan emitir dinero, las criptomonedas son solo el intento más
+reciente. Pero a diferencia de los depósitos bancarios, tal dinero no es
+comúnmente aceptado como medio de intercambio. Esto también sucede con
+Bitcoin, la criptomoneda más aceptada. Un impedimento a su utilidad como
+medio de intercambio es la alta volatilidad de su valor. Una respuesta
+reciente a este problema fue la aparición de las llamadas monedas
+estables. Las monedas estables generalmente intentan estabilizar su
+valor en una de las dos maneras siguientes: o bien imitando a los bancos
+centrales (monedas estables algorítmicas) o bien imitando a los bancos
+comerciales o a los medios de inversión (monedas estables con respaldo
+de activos).\footnote{Para más detalles sobre la taxonomía y descripción
+de las monedas estables véase~\citet{Bullmann}.}
+
+Las ``monedas estables algorítmicas'' dependen de algoritmos para
+regular su suministro. En otras palabras, intentan alcanzar la
+estabilidad de su precio con sus propias ``políticas monetarias
+algorítmicas''. Hay ejemplos de tales monedas estables (p. ej. Nubits),
+pero hasta ahora ninguna ha estabilizado su valor por largo tiempo.
+
+Las monedas estables ``respaldadas con activos'' difieren en función del
+tipo de activos que usan y de los derechos legales que adquieren los
+titulares de monedas estables. Los tipos de activos que típicamente se
+usan son: dinero (reservas del banco central, billetes o depósitos en
+bancos comerciales), productos básicos (p. ej. oro), valores y a veces
+otras criptomonedas. Cuán bien tal esquema estabilice el valor de las
+monedas en relación al activo o los activos subyacentes depende de
+manera crucial de los derechos legales que adquieran los titulares de
+las monedas estables. Si una moneda estable es canjeable a un precio
+fijo (p. ej. 1 moneda = 1 USD, o 1 moneda = 1 onza de oro), tal
+estabilidad teóricamente se conseguirá.\footnote{Si también estabilice o
+no el valor de las monedas estables en relación con los bienes y
+servicios negociados depende de la estabilidad del valor del respectivo
+activo en relación con el valor de los bienes y servicios.} Lo que el esquema
+esencialmente hace es replicar a los bancos comerciales garantizando la
+convertibilidad al activo subyacente a la vista. Sin embargo, a
+diferencia de los depósitos bancarios, que típicamente están solo
+parcialmente respaldados por las reservas monetarias del banco central,
+las monedas estables generalmente están respaldadas completamente por
+las reservas del activo subyacente para evitar el riesgo de liquidez,
+principalmente porque carecen de beneficios públicos tales como el
+soporte de seguros de depósito y prestamistas de última instancia, que
+se aplican en cambio a los bancos regulados.
+
+Las monedas estables respaldadas con dinero se llaman también monedas
+estables fiduciarias. Sin embargo, mantener el 100\% de garantía en
+dinero (billetes o depósitos bancarios) no es muy rentable. En
+consecuencia, los proveedores de monedas estables tienen un incentivo
+para economizar su tenencia de activos y trasladarse hacia un sistema de
+reserva fraccionado, tal como lo hicieron los bancos comerciales.\footnote
+{La incertidumbre sobre si un moneda estable está
+totalmente garantizada puede ser una de las razones por las que una
+moneda estable puede negociarse por debajo de la par en el mercado
+secundario~\cite[véase][]{Lyons}. Este fue
+también históricamente el caso con los billetes cuando eran emitidos
+por los bancos comerciales. Tales billetes solían negociarse con
+diversos descuentos en el mercado secundario antes de que la emisión
+de billetes fuera nacionalizada y transferida al monopolio de los
+bancos centrales.} Esto implica que reducen su tenencia de activos de
+bajo rendimiento al mínimo que se considere necesario para satisfacer el
+requisito de convertibilidad. Añadiendo en cambio activos líquidos de
+alto rendimiento tales como bonos del Estado. Esto mejora la
+rentabilidad pero también incrementa el nivel de riesgo.
+
+Sin embargo, incluso si una moneda estable está garantizada al 100\% por
+un depósito en un banco comercial, sigue expuesta a los riesgos de
+crédito y liquidez del banco subyacente. Este riesgo se puede eliminar
+si los depósitos se mantienen en el banco central para que la moneda
+estable esté respaldada por las reservas del banco central. Tales
+monedas estables han sido llamadas ``CBDC sintéticas''~\cite{Adrian}.
+Es importante señalar, sin embargo, que tales
+monedas estables no son dinero del banco central y por lo tanto no son
+CBDC, ya que no constituyen obligaciones del banco central y, por lo
+tanto, siguen expuestas al riesgo de contraparte, es decir, el riesgo de
+que el emisor de la moneda estable se declare en quiebra.
+
+Si una moneda estable no es canjeable a un precio fijo, su estabilidad
+no está garantizada por el activo subyacente. Si la moneda estable a
+pesar de esto representa una participación en la propiedad del activo
+subyacente, el esquema se asemeja a un fondo de inversión fijo o a un
+fondo cotizado en bolsa (Exchange-Traded Fund - ETF), y se aplican los
+correspondientes riesgos. El valor de la moneda dependerá del valor neto
+de los activos del fondo, pero su valor real puede desviarse. Si hay
+participantes autorizados que puedan crear y canjear monedas estables y
+así actuar como arbitristas, como en el caso de los ETF y como estaba
+previsto para Diem~\cite{Libra}, es probable que la
+desviación sea mínima.
+
+En general, las monedas estables tiene una mayor probabilidad de llegar
+a convertirse en dinero que las criptomonedas, especialmente si se
+regulan adecuadamente. Sin embargo, la disponibilidad de CBDC limitaría
+significativamente su utilidad.
+
+\section{Diseños simplistas de CBDC} \label{3.-diseuxf1os-simplistas-de-cbdc}
+
+Como se ha señalado, una CBDC sería una obligación del banco central.
+Dos posibles diseños que se analizan en la literatura son: (a) una CBDC
+basada en cuentas y (b) una CBDC basada en tokens (o basada en valor).
+Estos diseños corresponden a los dos tipos existentes de dinero de un
+banco central y sus correspondientes sistemas de pago (Kahn \& Roberds
+2008): las reservas de un banco central (en un sistema basado en
+cuentas) y billetes (en un sistema basado en tokens). Un pago se produce
+si un activo monetario se transfiere de un pagador a un beneficiario. En
+un sistema basado en cuentas, una transferencia se produce cobrándole a
+la cuenta del pagador y transfiriendo el crédito a la cuenta del
+beneficiario. En un sistema basado en tokens, la transferencia se
+produce transfiriendo el valor en sí o el token, es decir, un objeto que
+representa el activo monetario. El mejor ejemplo de un token es el
+efectivo -- monedas o billetes. Pagar con efectivo significa entregar
+una moneda o un billete. No es necesario registrar la transferencia, la
+posesión del token es suficiente. Por lo tanto, las partes no están
+obligadas a revelar sus identidades en ningún momento durante la
+transacción, ambas pueden permanecer anónimas. De todas maneras, el
+beneficiario tiene que poder verificar la autenticidad del token. Esta
+es la razón por la que los bancos centrales invierten mucho en elementos
+de seguridad para los billetes.
+
+Ha habido sugerencias de que la distinción entre los sistemas basados en
+cuentas y los sistemas basados en tokens no es aplicable a las monedas
+digitales~\cite{Garratt}. Nosotros tenemos una opinión diferente
+porque creemos que hay una diferencia significativa. La distinción
+fundamental es la información contenida en el activo. En un sistema
+basado en cuentas, los activos (las cuentas) se asocian con los
+historiales de las transacciones, que incluyen todas las operaciones de
+crédito y débito de las cuentas. En un sistema basado en tokens, los
+activos (tokens) incluyen información acerca de su valor y de la entidad
+que emitió el token. Por tanto, la única posibilidad de lograr la
+propiedad de privacidad de la transacción como la que se obtiene con el
+dinero efectivo reside en los sistemas basados en tokens.\footnote
+{Si bien el término ``Bitcoin'' sugiere el uso de tokens, Bitcoin es un
+sistema basado en cuentas. La única diferencia entre un sistema
+tradicional basado en cuentas y una blockchain es que las cuentas no
+se guardan en una base de datos central, sino en una base de datos
+descentralizada del tipo ``solo por anexión''.}
+
+\subsection{CBDC basada en cuentas}\label{cbdc-basada-en-cuentas}
+
+La forma más simple de lanzar una CBDC sería permitir que el público
+tenga cuentas de depósito en el banco central. Esto implica que el banco
+central seria responsable de llevar a cabo verificaciones para conocer a
+sus clientes (Know-Your-Customer - KYC) y asegurar el cumplimiento del
+AML y CFT. Esto incluiría no solo realizar el proceso inicial del KYC,
+sino también autentificar a los clientes para las transacciones
+bancarias, gestionar el fraude y lidiar con los falsos positivos y las
+autenticaciones de los falsos negativos. Dada la limitada presencia
+física de bancos centrales en la sociedad, y el hecho de que la
+autenticación del ciudadano es algo que probablemente en la actualidad
+los bancos no estén preparados para hacer a gran escala, cualquier CBDC
+basada en cuentas requeriría que el banco central delegara estas
+verificaciones. Todo el servicio y mantenimiento de tales cuentas podría
+asignarse a proveedores externos~\cite{Bindseil}, o la legislación
+podría obligar a los bancos comerciales a abrir cuentas bancarias en el
+banco central para sus clientes~\cite{Berentsen}.
+
+Tal CBDC basada en cuentas daría potencialmente a un banco central mucha
+información. Una posible preocupación podría ser que esto permitiera a
+los gobiernos realizar fácilmente vigilancia masiva e imponer sanciones
+a los titulares de cuentas individuales. Su naturaleza centralizada hace
+que tales intervenciones sean económicas y fáciles de aplicar contra
+individuos o grupos. Incluso en las democracias, hay muchos ejemplos de
+abusos de vigilancia dirigidos a críticos y opositores políticos. Se
+podría argumentar que los bancos centrales independientes puedan
+salvaguardar tal información del escrutinio del gobierno y el abuso
+político, pero esto solo abriría una nueva vía para la presión política,
+amenazando la independencia del banco central. Además, la base de datos
+central sería un objetivo importante para los atacantes: incluso el
+acceso de solo lectura a partes de la base de datos podría crear riesgos
+significativos para las personas cuyos datos fueran expuestos.
+
+Proveyendo cuentas bancarias al público, un banco central estaría
+también en competición directa con los bancos comerciales. Esta
+competición implicaría dos riesgos. Primero, podría amenazar la base de
+depósitos de los bancos y, en el extremo, desintermediar el sector
+bancario. Esto podría afectar de manera adversa la disponibilidad de
+crédito para el sector privado y, como resultado, la actividad
+económica~\cite{Agur}. La desintermediación de los bancos también podría
+conducir a la centralización del proceso de asignación de crédito dentro
+del banco central, lo que afectaría negativamente la productividad y el
+crecimiento económico. En segundo lugar, permitir que la gente traslade
+sus depósitos al refugio seguro de un banco central podría acelerar las
+caídas bancarias durante crisis financieras.
+
+Existen sin embargo argumentos contrarios. \citet{Brunnermeier} argumentan
+que la transferencia de fondos desde un
+depósito hacia una cuenta de CBDC conduciría a una sustitución automática de
+la financiación de depósitos por la financiación del banco central,
+simplemente haciendo explicita la garantía implícita del banco central como
+prestamista de última instancia. \citet{Berentsen}
+sostienen que la competencia de los bancos centrales podría incluso tener un
+efecto disciplinario sobre los bancos comerciales y, por lo tanto, incrementar
+la estabilidad del sistema financiero, ya que los bancos comerciales tendrían
+que hacer sus modelos de negocio más seguros para evitar las caídas bancarias.
+
+También hay propuestas para mitigar el riesgo de la desintermediación
+que tienen como objetivo limitar o desincentivar el uso de CBDC como
+depósito de valor. Una propuesta es limitar la cantidad de CBDC que se
+puede poseer. Una segunda propuesta es aplicar una tasa de interés
+ajustable a las cuentas de CBDC, de manera que la remuneración esté
+siempre lo bastante por debajo de la remuneración de las cuentas de los
+bancos comerciales (posiblemente incluyendo un rendimiento negativo)
+para hacer que las CBDC resulten menos atractivas como depósitos de
+valor~\cite{Kumhof,Bindseil}. Además, para disuadir las
+caídas bancarias, \citet{Kumhof} sugieren que las CBDC no
+deberían ser emitidas contra depósitos bancarios, sino solo contra
+valores tales como bonos del Estado. En general, una CBDC basada en
+cuentas requeriría un análisis más profundo de estas cuestiones.
+
+
+\subsection{CBDC basada en tokens y dependiente del hardware}
+\label{cbdc-basada-en-tokens-y-dependiente-del-hardware}
+
+Un banco central podría también emitir tokens electrónicos en lugar de
+cuentas. Técnicamente esto requiere de un sistema para asegurar que los tokens
+electrónicos no se puedan copiar fácilmente. Las funciones físicamente
+imposibles de clonar~\cite[véase][]{Katzenbeisser} y las zonas seguras en
+el hardware~\cite[véase][]{Alves,Pinto} son dos tecnologías potenciales para
+la prevención de la copia digital. Las funciones físicas imposibles de clonar,
+sin embargo, no se pueden intercambiar a través de Internet (eliminando así el
+uso principal de las CBDC), y anteriores funciones de seguridad en el hardware
+para la prevención de copias se han visto comprometidas
+repetidamente~\cite[véase p. ej.][]{Wojtczuk,Johnston,Lapid}.
+
+Una ventaja fundamental de las CBDC basadas en tokens sobre las basadas
+en cuentas del banco central es que los sistemas basados en tokens
+funcionarían sin conexión, es decir, los usuarios podrían intercambiar
+tokens (peer-to-peer) sin involucrar al banco central, lo que protegería
+la privacidad y la libertad de las personas. Sin embargo, la
+desintermediación que se produce cuando los usuarios pueden intercambiar
+tokens electrónicos sin los bancos como intermediarios que realizan los
+controles KYC, AML y CFT dificultarían la limitación de los abusos por
+parte de delincuentes.
+
+Las tarjetas SIM son actualmente las candidatas más extensivamente
+disponibles para un sistema de pago seguro basado en hardware, pero
+estas también conllevan riesgos. La experiencia~\cite[véase p. ej.][]{Soukup,Garcia,Kasper,CCC} sugiere
+que cualquier dispositivo económicamente producible que almacene tokens
+con un valor monetario en posesión de una persona, y que permita
+transacciones sin conexión -- y por tanto el robo de la información que
+contiene -- será el objetivo de ataques de falsificación exitosos tan
+pronto como el valor económico del ataque fuera los suficientemente
+elevado. Tales ataques incluyen usuarios que atacan su propio
+hardware~\cite[véase también]{Allen}. Los sistemas de pago con tarjeta que
+se han desplegado previamente dependen de la resistencia a la
+manipulación en combinación con la detección del fraude para limitar el
+impacto de una situación de peligro. Sin embargo, la detección del
+fraude requiere la habilidad de identificar a los pagadores y seguir la
+pista de los clientes, lo cual no es compatible con la privacidad de la
+transacción.
+
+\section{Diseño de CBDC basado en tokens para salvaguardar la
+privacidad}
+\label{4.-diseuxf1o-de-cbdc-basado-en-tokens-para-salvaguardar-la-privacidad}
+
+La CBDC que se propone aquí es de tipo ``solo software'', simplemente una
+aplicación para teléfonos inteligentes que no requiere ningún hardware
+adicional por parte de los usuarios. La CBDC se basa en eCash y GNU
+Taler. Taler es parte del Proyecto GNU, cuyo fundador, Richard Stallman, acuñó
+el término \emph{Software Libre}, actualmente denominado \emph{Software Libre
+y de Código Abierto} (Free/Libre Open Source Software -- FLOSS).\footnote{Para
+más información sobre GNU, véase \url{https://www.gnu.org} y
+\citet{Stallman}. GNU Taler se publica gratuitamente bajo la Licencia Pública
+General Affero del Proyecto GNU. Otros programas del Proyecto GNU populares
+entre los economistas son «R» y ``GNU Regression, Econometrics and Time-series
+Library'' (GRETL). Un análisis de los beneficios del FLOSS en comparación con
+el software privativo en el campo de la investigación puede consultarse
+en~\citet{Baiocchi}, \citet{Yalta2008} y \citet{Yalta2010}. Sobre el
+licenciamiento de código abierto véase \citet{Lerner}.} Un programa se
+considera ``Software Libre'' si la licencia otorga a los usuarios cuatro
+libertades esenciales: la libertad de ejecutar el programa como deseen, la
+libertad de estudiar el programa y modificarlo, la libertad de redistribuir
+copias del programa y la libertad de distribuir copias de las versiones
+modificadas del programa. El software libre no tiene por qué ser no
+comercial: proporcionar soporte técnico para software es un modelo de negocio
+estándar para el FLOSS.
+
+Dado el gran número de partes interesadas involucradas en una CBDC al
+por menor (el banco central, el sector financiero, comerciantes y
+clientes) y la importancia crítica de la infraestructura, una CBDC al
+por menor debe basarse en el FLOSS. Imponer una solución propietaria que
+requiera la dependencia de un proveedor en particular sería
+probablemente un obstáculo para la adopción desde el principio. Con el
+FLOSS, todas las partes interesadas tienen acceso a cada detalle de la
+solución y el derecho de adaptar el software a sus necesidades. Esto
+conduce a una integración más fácil y una mejor interoperabilidad y
+competencia entre proveedores.\footnote{Sin embargo, puede haber otros
+roles para hardware privado. Por ejemplo, proteger los depósitos de
+claves y ciertas funciones de auditoría, en la medida en que tal
+seguridad pueda demostrarse solo como aditiva, puede ser un área donde
+el hardware dedicado evaluado por solo un número limitado de expertos
+podría tener ventajas.} Además, permite que el banco central cumpla
+con los requisitos de transparencia y responsabilidad. Los beneficios
+del FLOSS para la seguridad son también ampliamente reconocidos. La
+disponibilidad del código fuente y el derecho a modificarlo facilitan la
+detección de fallos y su rápida solución.\footnote{Por ejemplo, un
+boletín de seguridad cibernética emitido por la Agencia de Seguridad
+Nacional de EE. UU. en abril de 2020 insta a los usuarios a priorizar
+el software de código abierto en la selección y el uso de servicios de
+colaboración para la comunicación por Internet: ``El desarrollo de
+código abierto puede proporcionar confiabilidad de que el código está
+escrito para asegurar las mejores prácticas de programación y no es
+probable que introduzca vulnerabilidades o debilidades que puedan
+poner en riesgo a los usuarios y los datos '' (U/OO/134598-20).}
+
+En esta nuestra arquitectura que proponemos todas las interacciones del
+consumidor y el comerciante son con bancos comerciales. Sin embargo, la
+creación de dinero y la base de datos las proporcionan exclusivamente el
+banco central. Los bancos comerciales autentican a los clientes cuando
+retiran CBDC y a los comerciantes/beneficiarios cuando reciben CBDC,
+pero cuando gastan CBDC, los clientes/pagadores solo tienen que
+autorizar sus transacciones y no necesitan identificarse. Esto hace que
+los pagos resulten más baratos, fáciles y rápidos, y evita una fácil
+interferencia con la privacidad~\cite{Dold}. Además, autenticar a los
+clientes cuando retiran CBDC y a los comerciantes/beneficiarios cuando
+reciben CBDC garantiza el cumplimiento del KYC, AML y CFT.
+
+La CBDC que se propone en el presente documento es un auténtico
+instrumento digital al portador porque cuando el usuario retira una suma
+de dinero en forma de número, el número es ``cegado'' u ocultado por el
+teléfono inteligente con un cifrado especial. En el sistema real, una
+moneda es un par de claves pública / privada, y la clave privada solo la
+conoce el propietario de la moneda.\footnote{En Bitcoin, que es un
+sistema basado en cuentas, el par de claves es una cuenta, siendo la
+clave pública la ``dirección'' de la cuenta y por tanto un tipo de
+``identidad'', incluso si se trata de un pseudónimo.} La moneda deriva
+su valor financiero de la firma del banco central en la clave pública de
+la moneda. El banco central hace la firma con su clave privada y dispone
+de múltiples pares de claves de denominación para la firma ciega de
+monedas de diferentes valores. Un comerciante puede utilizar la
+correspondiente ``clave pública'' del banco central para verificar la
+firma. Sin embargo, para asegurarse de que la moneda no haya sido
+copiada y ya canjeada por otro beneficiario (es decir, que no se haya
+``gastado dos veces''), el comerciante debe depositar la moneda para que
+el banco central pueda comparar la moneda con un archivo de monedas
+canjeadas. Debido a que ni el banco comercial ni el banco central ven el
+número de la moneda durante el retiro, más tarde, cuando el comerciante
+deposita la moneda, se desconoce qué usuario la retiró. El cegamiento y
+la privacidad resultante son los que hacen de este tipo de CBDC un
+verdadero instrumento digital al portador.
+
+En el análisis que sigue proporcionamos una introducción de alto nivel a
+la tecnología y demostramos cómo se puede integrar con el sistema
+bancario existente para crear una CBDC. \citet{Dold} describe detalles
+adicionales.
+
+\subsection{Componentes fundamentales}\label{componentes-fundamentales}
+
+A continuación describimos los principales componentes del protocolo,
+incluido el trasfondo matemático para una posible instanciación de las
+primitivas criptográficas utilizadas, para ilustrar cómo podría
+funcionar una implementación. Observamos que existen diseños matemáticos
+alternativos y equivalentes para cada componente, y simplemente
+presentamos los diseños seguros más sencillos de los que tenemos
+conocimiento.
+
+\emph{Firmas digitales.} La idea básica de las firmas digitales en un esquema
+de firma con clave pública es que el propietario de una clave privada es el
+único que puede firmar un mensaje, mientras que la clave pública permite a
+cualquiera verificar la validez de la firma.\footnote{La criptografía de clave
+pública fue introducida por~\citet{Diffie}, y la primera implementación de
+firmas digitales fue introducida por~\citet{Rivest}.} El resultado de la
+función de verificación es la declaración binaria ``verdadero'' o ``falso''. Si el
+mensaje está firmado con la clave privada que pertenece a la clave pública de
+verificación, el resultado es verdadero, de lo contrario es falso. En nuestra
+propuesta, el mensaje es una ``moneda'' o ``billete'' con un número de serie, y la
+firma del banco central confirma su validez. Si bien GNU Taler usa por defecto
+firmas EdDSA modernas~\cite[véase][]{Bernstein2012}, presentamos un esquema de
+firma criptográfica simple basado en el bien estudiado sistema criptográfico
+RSA~\cite{Rivest}.\footnote{Para un análisis de la larga historia del
+criptosistema RSA y un estudio de los ataques al criptosistema RSA,
+consulte~\citet{Boneh}.} Sin embargo, en principio se puede utilizar cualquier
+esquema de firma criptográfica (DSA, ECDSA, EdDSA, RSA, etc.).
+
+Para generar las claves RSA, el firmante elige primero dos grandes e
+independientes números primos $p$ y $q$ y calcula $n = \emph{pq}$
+así como la función totient de Euler
+$\phi(n) = (p - 1)(q - 1)$.
+Entonces, cualquier $e$ con $1 < e < \phi(n)$ y
+$\gcd(e, \phi(n)) = 1$ se puede usar para
+definir una clave pública $(e,n)$. La condición de que el
+máximo común divisor (greatest common divisor - $\gcd$) de $e$ y
+$\phi(n)$ tiene que ser 1 (p. ej., que deben ser
+relativamente primos) asegura que la inversa de
+$e \mod \phi(n)$ existe.
+Esta inversa es la
+correspondiente clave privada $d$. Dado $\phi(n)$, la clave
+privada $d$ se puede calcular usando el algoritmo extendido
+Euclídeo de modo que
+$d \cdot e \equiv 1 \mod \phi(n)$.
+
+Dada la clave privada $d$ y la clave pública $(e, n)$, una firma simple RSA
+$s$ sobre un mensaje $m$ es
+$s \equiv m^{d} \mod n$.
+Para verificar la firma, se calcula
+$m' \equiv s^{e} \mod n$.
+Si $m'$ y $m$ coinciden, la firma es válida, lo que prueba que el
+mensaje fue firmado con la clave privada que pertenece a la clave
+publica de verificación (autenticación de mensaje) y que ese mensaje no
+ha sido cambiado en tránsito (integridad de mensaje). En la práctica,
+las firmas se colocan sobre lo hashes de los mensajes en vez de los
+propios mensajes. Las funciones hash calculan el resumen de los
+mensajes, que son identificadores únicos y cortos para los mensajes.
+Firmar un hash corto es mucho más rápido que firmar un mensaje largo, y
+la mayoría de los algoritmos de firma solo funcionan con entradas
+relativamente cortas.\footnote{En el caso del criptosistema RSA el
+límite de la longitud es $\log_{2}n$ bits.}
+
+\emph{Firmas ciegas.} Usamos firmas ciegas, introducidas
+por~\citet{Chaum1983}, para proteger la privacidad de los compradores. Una
+firma ciega se usa para crear una firma criptográfica para un mensaje sin que
+el firmante conozca el contenido del mensaje que se firma. En nuestra
+propuesta, esto evita que los bancos comerciales y el banco central puedan
+rastrear las compras identificando a los compradores. Nuestra propuesta
+funciona en principio con cualquier esquema de firma ciega, pero la mejor
+solución es la variante basada en RSA descrita por~\citet{Chaum1983}.
+
+El cegamiento lo realizan los clientes, quienes ciegan sus monedas antes
+de transmitirlas al banco central para ser firmadas. Los clientes por
+tanto no necesitan confiar al banco central la protección de su
+privacidad. Además, el cegamiento RSA proveería de protección de la
+privacidad incluso contra ataques informáticos cuánticos. El banco
+central, por su parte, establece múltiples denominaciones de pares de
+claves disponibles para realizar la firma ciega de monedas con
+diferentes valores, y publica/provee las correspondientes claves
+públicas $(e, n)$ para estos valores.
+
+Sea $f$ el valor hash de una moneda y por tanto un identificador único
+para esta moneda. El cliente que retira la moneda primero genera una
+factor ciego aleatorio $b$ y calcula
+$f' \equiv fb^{e} \mod n$
+con la clave pública del banco central para ese valor.
+La moneda cegada $f'$ se transmite luego
+al banco central para ser firmada. El banco central firma $f'$ con su
+clave privada $d$ calculando la firma ciega
+$s' \equiv \left(f' \right)^{d} \mod n$ y devuelve
+$s'$ al cliente.
+El cliente puede entonces des-cegar la firma calculando
+$s \equiv s'b^{- 1} \mod n$.
+Esto funciona porque
+$\left( f' \right)^d = f^db^{ed} = f^db$ y, así,
+multiplicar $s'$ con $b^{- 1}$ produce $f^d$, que es una firma RSA
+válida sobre $f$ como antes:
+$s^e \equiv f^{de} \equiv f \mod n$.
+
+En la propuesta original de Chaum, las monedas eran solo tokens. Sin
+embargo, nosotros queremos que los consumidores puedan realizar
+contratos usando firmas digitales. Para lograrlo, cuando una billetera
+digital retira una moneda, primero crea una clave privada aleatoria
+$c$ y calcula la correspondiente clave publica $C$ de esta moneda
+para crear firmas digitales con esquemas de firma criptográfica
+regulares (como DSA, ECDSA, EdDSA y RSA). Entonces, se deriva $f$
+usando una hash criptográfica de la clave pública $C$, que luego es
+firmada en modalidad ciega por el banco central (usando un factor
+aleatorio ciego actualizado para cada moneda). Ahora el cliente puede
+usar $c$ para firmar compras electrónicamente, gastando así la moneda.
+
+Como se ha señalado anteriormente, el banco central establecería pares
+de claves para los diferentes valores de las monedas y publicaría las
+claves públicas que los clientes podrían usar para retirar dinero. Estas
+claves de denominación, y por tanto las monedas, tendrían una fecha de
+vencimiento antes de la cual deberían ser gastadas o intercambiadas por
+nuevas monedas. A los clientes se les daría una cierta cantidad de
+tiempo durante el cual podrían intercambiar sus monedas. Un proceso
+similar existe para los billetes físicos, donde las series de los
+billetes se renuevan regularmente para que los billetes vayan equipados
+con las últimas características de seguridad, excepto que los billetes
+generalmente permanecen en circulación durante décadas en vez de por
+unos pocos meses o años.\footnote{En Suiza, por ejemplo, el Swiss
+National Bank empezó la eliminación paulatina la serie octava de
+billetes en abril de 2016. Estos billetes fueron puestos en
+circulación al final de los 90. A partir del día 1 de enero de 2020,
+sin embargo, todos los billetes que empiezan por la serie sexta
+emitidos en 1976, así como cualquier futura serie, permanecen válidas
+y se pueden cambiar por billetes actuales de forma indefinida.}
+
+Desde un punto de vista técnico, una fecha de vencimiento tiene dos
+ventajas. Primero, mejora la eficiencia del sistema porque el banco
+central puede descartar entradas vencidas y no tiene que almacenar y
+buscar una lista siempre creciente de monedas (gastadas) para detectar
+el doble gasto. Segundo, reduce los riesgos de seguridad porque el banco
+central no tiene que preocuparse sobre ataques contra sus claves
+($d$) de denominación (privadas) vencidas. Además , incluso si una
+clave privada se ve comprometida, el tiempo durante el cual el atacante
+puede usar la clave es limitado. Además cobrar una comisión por el
+cambio permitiría al banco central implementar tasas de interés
+negativas, si se considera necesario. El banco central podría también
+imponer un límite de conversión por cliente en consideración del AML y
+el CFT (límites de ``efectivo'') o por razones de estabilidad
+financiera (para prevenir el acaparamiento o las caídas bancarias), si
+así se deseara.
+
+\emph{Protocolo de intercambio de claves.} GNU Taler utiliza un
+protocolo de intercambio de claves de manera inusual para proporcionar
+un vínculo entre la moneda original y el cambio (también llamado
+``vuelto'') entregado por esa moneda original. Esto asegura que siempre
+se pueda entregar el cambio sin comprometer la transparencia de los
+ingresos o la privacidad del consumidor. El mismo mecanismo se puede
+usar también para realizar devoluciones anónimas a los clientes. El
+protocolo también maneja fallos en la red y en los componentes,
+asegurando que los pagos se hayan realizado definitivamente o se hayan
+cancelado definitivamente y que todas las partes tengan una prueba
+criptográfica del resultado. Esto es aproximadamente equivalente a los
+intercambios atómicos de los protocolos \emph{interledger} o al
+intercambio justo en sistemas tradicionales de efectivo electrónico.
+
+La construcción matemática más común para un protocolo de intercambio de
+claves es la construcción Diffie-Hellman~\cite{Diffie}. Esta
+permite que dos partes puedan derivar una clave secreta compartida. Para
+hacerlo, comparten dos parámetros del dominio $p$ y $g$, que
+pueden ser públicos, donde $p$ es un número primo grande y $g$
+es una raíz primitiva módulo $p$.\footnote{Un entero $g$ es una raíz
+primitiva módulo $p$ si para cada entero $a$ coprimo a $p$ hay
+algún entero $k$ para el cual
+$g^k \equiv a \mod p$.
+En la práctica, $g$ debería ser tal raíz primitiva $p-1$, que se
+llama también generador, para prevenir ataques de subgrupo tales como ataques
+Pohlig-Hellman~\cite[véase][]{Lim}.} Ahora, las dos partes eligen sus claves
+privadas \emph{a} y \emph{b}, que son dos números enteros grandes. Con estas claves
+privadas y los parámetros del dominio, generan sus respectivas claves
+públicas $A \equiv g^{a} \mod p$ y $B \equiv g^{b} \mod p$.
+Cada una de las partes ahora puede usar su propia clave privada y la
+clave pública de la otra parte para calcular la clave secreta compartida
+$k \equiv \left( g^b \right)^{a} \equiv \left( g^{a} \right)^{b} \equiv g^{\text{ab}} \mod p$.
+\footnote{El mismo mecanismo también se podría usar para garantizar que
+las monedas no se transfieran a un tercero durante el retiro. Para
+lograr esto, los consumidores tendrían que salvaguardar una clave de
+identidad a largo plazo. Luego, el proceso de retiro podría usar la
+misma construcción que usa GNU Taler para obtener el cambio, excepto
+que se usaría la clave de identidad a largo plazo de un cliente en
+lugar de la moneda original cuando se retira de la cuenta bancaria del
+cliente. Sin embargo, si el cliente no proteje la clave de identidad a
+largo plazo las garantías de privacidad podrían quedar anuladas con
+consecuente riesgo de robo de todas las monedas restantes. Dado el
+riesgo limitado en las transferencias a terceros al retirar monedas,
+no está claro si esta mitigación sería una buena compensación.}
+
+Para obtener el cambio (también llamado ``vuelto''), el cliente empieza
+con la clave privada de la moneda $c$. gastada parcialmente. Sea $C$ la
+correspondiente clave pública, p. ej.
+$C = g^{c} \mod p$.
+Cuando la moneda se gastó parcialmente, el banco central grabó en su base de
+datos la transacción en la que se incluye a $C$. Para simplificar, daremos
+por sentado que existe una denominación que coincide exactamente con el
+valor residual. De no ser así, se puede simplemente ejecutar
+repetidamente el protocolo de cambio hasta obtener todo el cambio
+necesario. Sea $(e,n)$ la clave de denominación para el
+cambio que se tiene que emitir.
+
+Para obtener el cambio, el cliente primero crea $\kappa$ claves de
+transferencia privada $t_{i}$ para
+$i \in \left\{ 1,\ldots,\kappa \right\}$ y calcula las
+correspondientes claves públicas $T_{i}$. Estas claves de
+transferencia $\kappa$ son simplemente pares de claves pública-privada
+que permiten al cliente ejecutar localmente el protocolo de intercambio
+de claves -- con el cliente jugando en ambos lados -- $\kappa$ veces
+entre $c$ y cada $t_{i}$. Si se usa Diffie-Hellman para el protocolo de
+intercambio de claves, tendremos
+$T_{i} \equiv g^{t_{i}} \mod p$.
+
+El resultado son tres secretos de transferencia
+$K_{i} \equiv \emph{KX}(c,t_{i})$. El protocolo de
+intercambio de claves se puede usar de diferentes maneras para llegar al
+mismo valor
+$K_{i} \equiv \emph{KX}(C,t_{i}) = \emph{KX}(c,T_{i})$.
+Dada $K_{i}$, el cliente usa una función criptográfica hash $H$ para
+derivar valores
+$(b_{i},c_{i}) \equiv H(K_{i})$, donde
+$b_{i}$ es un factor ciego válido para la clave de denominación
+$(e,n)$ y $c_{i}$ es una clave privada para obtener la
+moneda recién creada como cambio. $c_{i}$ debe ser adecuada tanto para
+crear firmas criptográficas como para su futuro uso con el protocolo de
+intercambio de claves (como $c$, para obtener cambio a partir del cambio).
+Sea $C_{i}$ la clave pública correspondiente a $c_{i}$. El cliente
+solicita entonces al banco central que cree una firma ciega sobre
+$C_{i}$ para $i \in \{ 1,\ldots,\kappa\}$.\footnote{Si se usara el
+criptosistema RSA para firmas ciegas, usaríamos
+$f \equiv \emph{FDH}_{n}(C_{i})$, donde
+$\emph{FDH}_{n}()$ es el hash de dominio completo sobre
+el dominio $n$.} En esta petición, el cliente también se compromete a
+las claves públicas $T_{i}$. La petición es autorizada usando una
+firma hecha con la clave privada $c$.
+
+En lugar de devolver directamente la firma ciega, el banco central
+primero desafía al cliente para comprobar que el cliente haya usado
+correctamente la construcción mencionada anteriormente proveyendo
+$\gamma \in \left\{ 1,\ldots,\kappa \right\}$. El cliente debe
+entonces revelar al banco central la $t_{i}$ para $i \neq \gamma$ .
+El banco central puede entonces calcular
+$K_{i} \equiv \emph{KX}(C,t_{i})$ y derivar los valores
+de $(b_{i},c_{i})$. Si para todas las $i \neq \gamma$
+la $t_{i}$ provista demuestra que el cliente usó la construcción
+correctamente, el banco central devuelve la firma ciega sobre
+$C_{\gamma}$. Si el cliente no provee una prueba correcta, se pierde
+el valor residual de la moneda original. Esto penaliza efectivamente a
+quienes intentan evadir la transparencia de sus ingresos con una tasa de
+impuestos estimada de $1 - \frac{1}{\kappa}$.
+
+Para evitar que un cliente conspire con un comerciante que está tratando
+de ocultar sus ingresos, el banco central permite que cualquiera que
+conozca $C$ pueda obtener, en cualquier momento, los valores de
+$T_{\gamma}$ y las correspondientes firmas ciegas de todas las monedas
+vinculadas a la moneda original $C$. Esto permite que el propietario de la
+moneda original -- que conoce $c$ -- calcule
+$K_{\gamma} \equiv \emph{KX}( c,T_{\gamma})$ y, a partir de
+allí, pueda derivar $(b_{i},c_{i})$ y descifrar la firma
+ciega. En consecuencia, un comerciante que oculte sus ingresos de este
+modo formaría básicamente una unión económica limitada con el cliente en
+lugar de obtener un control exclusivo.
+
+\hypertarget{arquitectura-del-sistema}{%
+\subsection{Arquitectura del sistema}\label{arquitectura-del-sistema}}
+
+El objetivo principal de nuestra arquitectura es asegurar que los bancos
+centrales no tengan que interactuar directamente con los clientes o
+guardar ninguna información sobre ellos, sino simplemente mantener una
+lista de las monedas que se gastan. La autenticación se delega a los
+bancos comerciales, que tienen ya la infraestructura necesaria. Los
+protocolos de retiro y depósito llegan al banco central a través del
+banco comercial como intermediario. Desde el punto de vista del cliente,
+el proceso es análogo a retirar dinero efectivo desde un cajero
+automático. La transacción entre el banco comercial del usuario y el
+banco central tiene lugar en segundo plano. El procedimiento para
+retirar CBDC sería como se muestra en la Figura~\ref{fig:fig1}.
+
+\begin{figure}[h!]
+ \includegraphics[width=\textwidth]{retirada.pdf}
+ \caption{Retiro de CBDC}
+ \label{fig:fig1}
+\end{figure}
+
+Un cliente (1) proporciona autenticación a su banco comercial usando la
+autenticación respectiva del banco comercial y los procedimientos de
+autorización. A continuación, el teléfono (u ordenador) del cliente
+obtiene la clave de denominación $(e, n)$ provista por el banco central
+para ese valor; calcula entonces (2) un par de claves para una moneda,
+con la clave privada c y la clave pública $C$, y elige un factor de cegado
+$b$. A la clave pública de la moneda se le aplica una función hash
+($\to$ $f$) y es cegada ($\to$ $f'$). A continuación, (3) el teléfono
+del cliente envía $f'$ junto con una autorización para retirar la
+moneda y debitar de la cuenta del cliente en el banco comercial a través
+de un canal seguro establecido. El banco comercial entonces (4) debita
+la cantidad en la cuenta de depósito del cliente, (5) autoriza
+digitalmente la petición con la propia firma digital de su sucursal
+bancaria y reenvía la petición y la moneda cegada al banco central para
+su firma. El banco central (6) deduce el valor de la moneda en la cuenta
+del banco comercial, firma la moneda de forma ciega con la clave privada
+del banco central para el valor respectivo, y (7) devuelve la firma
+ciega $s'$ al banco comercial. (8) reenvía la firma ciega $s'$
+a la billetera electrónica del cliente. Finalmente, el teléfono del
+cliente (9) usa $b$ para descifrar la firma ($\to$ $f$) y almacena la
+moneda recién acuñada $(c, s)$.
+
+Cuando se gastan CBDC, el proceso es análogo a pagar al vendedor en
+efectivo. Sin embargo, para asegurar el acuerdo, el vendedor debe
+depositar las monedas. El procedimiento para gastar CBDC se indica en la
+Figura~\ref{fig:fig2}.
+
+Un cliente y un vendedor negocian un contrato comercial, y (1) el
+cliente usa una moneda electrónica para firmar el contrato o factura de
+venta con la clave privada $c$ de la moneda y transmite la firma al
+vendedor. La firma de una moneda en un contrato con una moneda válida es
+una instrucción del cliente para pagar al vendedor que es identificado
+por la cuenta bancaria en el contrato. Los clientes pueden firmar
+contratos con múltiples monedas en caso de que una sola moneda fuera
+insuficiente para pagar la cantidad total. El vendedor (2) valida
+entonces la firma de la moneda sobre el contrato y la firma s del banco
+central sobre $f$ que corresponde a la $C$ de la moneda con las
+respectivas claves públicas y reenvía la moneda firmada (junto con la
+información de la cuenta del vendedor) al banco comercial del vendedor.
+El banco comercial del vendedor (3) confirma que el vendedor es uno de
+sus clientes y envía la moneda firmada al banco central. El banco
+central (4) verifica las firmas y comprueba su base de datos para
+asegurar que la moneda no haya sido previamente gastada. Si todo está en
+orden, (5) el banco central añade la moneda a la lista de monedas
+gastadas, acredita la cuenta del banco comercial en el banco central y
+(6) envía la confirmación al banco comercial a tal efecto. A
+continuación, (7) el banco comercial acredita la cuenta del vendedor e
+(8) informa al vendedor. El vendedor (9) entrega el producto o servicio
+al cliente. Todo el proceso dura solo unos pocos milisegundos.
+
+\begin{figure}[h!]
+ \includegraphics[width=\textwidth]{deposito.pdf}
+ \caption{Gastar y depositar CBDC}
+ \label{fig:fig2}
+\end{figure}
+
+\hypertarget{consideraciones-acerca-de-la-seguridad}{%
+\subsection{Consideraciones acerca de la Seguridad}
+\label{consideraciones-acerca-de-la-seguridad}}
+
+Nuestra propuesta requiere que el banco central opere un servicio en
+línea y una base de datos de alta disponibilidad. Debido a que los
+usuarios pueden copiar las monedas electrónicas, solo los controles en
+línea pueden prevenir eficientemente el doble gasto. Si bien existen
+soluciones teóricas para identificar de manera retroactiva a usuarios
+que se dediquen al doble gasto~\cite[véase][]{Chaum1990}, tales
+soluciones crean un riesgo económico tanto para los usuarios como para
+el banco central, debido al retraso en la identificación de
+transacciones fraudulentas. La detección del doble gasto en línea
+elimina este riesgo, pero a su vez implica que las transacciones serán
+imposibles de realizar si la conexión con el banco central no estará
+disponible.
+
+El banco central también tendrá que proteger la confidencialidad de las
+claves privadas que utiliza para firmar las monedas y otros mensajes del
+protocolo. De manera que si las claves de las firmas del banco central
+se vieran en algún momento comprometidas, como por ejemplo por una
+computadora cuántica, un ataque físico en su centro de datos, o quizás
+por algún nuevo algoritmo imprevisto, los usuarios puedan de forma
+segura, y sin comprometer su privacidad, ser reembolsados con todas las
+monedas que no han gastado. El banco central anunciaría la revocación de
+clave mediante la API (Application Programming Interface), que sería
+detectada por las billeteras e iniciarían el siguiente protocolo de
+actualización: el usuario revela al banco central la clave pública
+$C$ de la moneda, la firma $s$ del banco central, y el factor
+ciego $b$, posibilitando así que el banco central verifique el
+retiro legítimo del usuario y devuelva el valor de la moneda no gastada.
+Para detectar un posible compromiso de esta clave, el banco central
+puede monitorear la base de datos en busca de casos de depósitos que
+superen los retiros.
+
+\subsection{Escalabilidad y Costes}\label{escalabilidad-y-costes}
+
+El esquema que proponemos sería tan eficiente y rentable como los
+modernos sistemas RTGS que utilizan actualmente los bancos centrales.
+
+La escalabilidad se refiere al costo de aumentar la capacidad de
+procesamiento para que se pueda procesar un número cada vez mayor de
+transacciones en un tiempo adecuado para la finalidad. El costo global
+del sistema puede ser bajo, ya que la CBDC que se propone aquí se basa
+en software solamente. Las monedas gastadas deben guardarse hasta que
+caduque el par de claves de denominación que se usó para firmar las
+monedas; por ejemplo, mediante un calendario anual renovable, que
+mantiene limitado el tamaño de la base de datos. La cantidad de potencia
+de procesamiento adicional y ancho de banda necesarios aumenta en la
+misma cantidad por cada transacción, gasto o depósito adicional, porque
+las transacciones son esencialmente independientes una de la otra. Esta
+potencia adicional se logra simplemente añadiendo más hardware,
+comúnmente llamado partición o fragmentación. Con el llamado hash
+consistente, las adiciones de hardware no tienen por qué ser
+disruptivas. Se puede utilizar cualquier tecnología de base de datos
+subyacente.
+
+Más concretamente, la lógica del front-end en el banco central solo tiene que
+realizar unas cuantas operaciones de firma, y un único procesador puede hacer
+miles de operaciones por segundo~\cite[véase][]{Bernstein2020}. Si un solo
+sistema es insuficiente, es fácil desplegar servidores front-end adicionales y
+solicitar a los varios bancos comerciales que balanceen sus peticiones en la
+granja de servidores o que utilicen un balanceador de carga para distribuir
+las peticiones dentro de la infraestructura del banco central.
+
+Los servidores front-end deben comunicarse con una base de datos para
+hacer transacciones y prevenir el doble gasto. Un solo servidor moderno
+para la base de datos debería ser suficiente para manejar de manera
+fiable decenas de miles de estas operaciones por segundo. Las
+operaciones se reparten fácilmente entre varios servidores de bases de
+datos simplemente asignando a cada servidor un rango de valores de los
+que es responsable. Este diseño asegura que las transacciones
+individuales nunca crucen fragmentos. Así, se espera que también los
+sistemas de back-end escalen linealmente con los recursos
+computacionales disponibles, de nuevo partiendo de una línea de base
+alta para un solo sistema.
+
+Los front-end también deben comunicarse con los back-end mediante una
+interconexión. Las interconexiones puede soportar grandes cantidades de
+transacciones por segundo. El tamaño de una transacción individual suele
+ser de 1-10 kilobytes aproximadamente. Así, las interconexiones de un
+centro de datos moderno, con velocidades de conmutación de 400 Gbit/s,
+pueden soportar millones de transacciones por segundo.
+
+En fin, el costo total del sistema es bajo. Es probable que el
+almacenamiento seguro de 1 a 10 kilobytes por transacción durante muchos
+años sea el costo predominante del sistema. Utilizando los precios de
+Amazon Web Services, experimentamos con un prototipo anterior de GNU
+Taler y descubrimos que el costo del sistema (almacenamiento, ancho de
+banda y computación) a escala estaría por debajo de USD 0,0001 por
+transacción (para obtener detalles sobre los datos, consulte~\citet{Dold}).
+
+
+\section{Consideraciones normativas y políticas}
+ \label{5.-consideraciones-normativas-y-poluxedticas}
+
+En el esquema propuesto, los bancos centrales no conocen la identidad de
+los consumidores o comerciantes ni los montos totales de las
+transacciones. Los bancos centrales solo ven cuándo se lanzan las
+monedas electrónicas y cuándo se canjean. Los bancos comerciales siguen
+proporcionando autenticación crucial de clientes y comerciantes y, en
+particular, siguen siendo los guardianes de la información del KYC. Los
+bancos comerciales observan cuándo los comerciantes reciben fondos y
+pueden limitar la cantidad de CBDC por transacción que un comerciante
+individual puede recibir, si así se requiere.
+
+Además, las transacciones están asociadas con los contratos pertinentes
+de los clientes. La transparencia de ingresos que se obtiene permite que
+el sistema cumpla con los requisitos del AML y CFT. Si se detectan
+patrones inusuales de ingresos comerciales, el banco comercial, las
+autoridades fiscales o las fuerzas del orden pueden obtener e
+inspeccionar los contratos comerciales subyacentes a los pagos para
+determinar si la actividad sospechosa es ilegal. La transparencia de los
+ingresos que se obtiene es también una fuerte medida contra la evasión
+fiscal porque los comerciantes no pueden declarar menos ingresos o
+evadir los impuestos sobre las ventas. En general, el sistema implementa
+privacidad por diseño y privacidad por omisión (como lo exige, por
+ejemplo, el Reglamento General de Protección de Datos de la Unión
+Europea). Los comerciantes no infieren inherentemente la identidad de
+sus clientes, los bancos solo tienen la información necesaria sobre las
+actividades de sus propios clientes y los bancos centrales están
+felizmente divorciados del conocimiento detallado de las actividades de
+los ciudadanos.
+
+Por razones reglamentarias, en algunos países existen límites para los
+retiros y pagos en efectivo. Dichas restricciones también podrían
+implementarse para la CBDC en el diseño propuesto. Por ejemplo, se
+podría limitar la cantidad que los consumidores puedan retirar por día,
+o limitar la cantidad total de CBDC que los bancos comerciales puedan
+convertir.
+
+Un problema potencial de estabilidad financiera que a menudo se plantea
+con las CBDC al por menor es la desintermediación del sector bancario.
+En particular, la venta de CBDC al por menor podría facilitar el
+acaparamiento de grandes cantidades de dinero del banco central. Esto
+podría afectar negativamente a la financiación de depósitos de los
+bancos porque el público tendría menos dinero en forma de depósitos
+bancarios. Para los países cuyas monedas sirven como monedas de refugio
+seguro, podría conducir a un aumento de las entradas de capital durante
+períodos de riesgo global, lo que resultaría en presiones adicionales en
+la apreciación del tipo de cambio.
+
+Si bien esto podría representar una preocupación seria en el caso de una
+CBDC basada en cuentas, la preocupación sería menor con una CBDC basada
+en tokens. En primer lugar, acumular una CBDC basada en tokens conlleva
+riesgos de robo o pérdida similares a los de acumular efectivo. Tener
+unos cientos de dólares en un teléfono inteligente es probablemente un
+riesgo aceptable para muchos, pero tener una cantidad muy grande es
+probablemente un riesgo menos aceptable. Por tanto, no esperaríamos un
+acaparamiento significativamente mayor que en el caso del efectivo
+físico.
+
+Sin embargo, si el acaparamiento o la conversión masiva a CBDC de dinero
+proveniente de depósitos bancarios se convirtieran en un problema, los bancos
+centrales tendrían varias opciones. Como se señaló, en el diseño propuesto los
+bancos centrales configuran una fecha de vencimiento para todas las claves de
+firma, lo que implica que en una fecha establecida las monedas firmadas con
+esas claves dejan de ser válidas. Cuando las claves de denominación caducan y
+los clientes tienen que cambiar monedas firmadas con claves de denominación
+antiguas por monedas nuevas, el regulador podría fácilmente imponer un límite
+de conversión por cliente para hacer cumplir un límite estricto a la cantidad
+de CBDC que cualquier individuo puede acumular. Además, los bancos centrales
+podrían cobrar una tarifa si fuera necesario. Una tarifa de actualización de
+este tipo, cuando las monedas están programadas para caducar, implicaría de
+hecho tasas de interés negativas en la CBDC, y haría que la CBDC resultara
+menos atractiva como depósito de valor, tal como sugiere Bindseil (2020). De
+hecho, sería la implementación directa de la idea de Silvio Gesell de aplicar
+un ``impuesto de posesión'' sobre la moneda, al que hace célebremente
+referencia~\citet{Keynes}, y reviven~\citet{Goodfriend}, \citet{Buiter}
+y~\citet{Agarwal}.
+
+En cuanto a las posibles implicaciones para las políticas monetarias, no
+anticipamos efectos materiales porque nuestra CBDC está diseñada para
+replicar el dinero en efectivo en lugar de los depósitos bancarios. La
+emisión, retiro y depósito de nuestra CBCD corresponden exactamente a la
+emisión, retiro y depósito de billetes. Es posible que una CBDC al por
+menor tenga un ritmo de circulación diferente a la del efectivo físico,
+pero esto no sería un problema material para las políticas monetarias.
+
+\hypertarget{trabajos-relacionados}{%
+\section{Trabajos relacionados}\label{6.-trabajos-relacionados}}
+
+Como se señaló anteriormente, la CBDC propuesta en el presente documento se
+basa en eCash y GNU Taler.\footnote{La implementación de eCash por la compañía
+DigiCash en los años noventa está documentada en
+\url{https://www.chaum.com/ecash}.} A partir de la propuesta original de Chaum
+para el efectivo electrónico, la investigación se ha centrado en tres
+cuestiones principales. Primero, en la propuesta original de Chaum las monedas
+tenían un valor fijo y solo podían gastarse en su totalidad. Pagar grandes
+cantidades con monedas denominadas en centavos sería ineficiente, por lo
+que~\citet{Okamoto}, \citet{Camenisch2005}, \citet{Canard} y~\citet{Dold}
+idearon formas de abordar este problema. Estas soluciones involucran
+protocolos para dar cambio o para posibilitar la divisibilidad de las monedas.
+
+Una segunda cuestión es que las transacciones a veces fallan debido a
+caídas de la red, por ejemplo. En este caso, el sistema debe permitir
+que los fondos permanezcan con el consumidor sin impacto negativo sobre
+privacidad. \citet{Camenisch2007} y~\citet{Dold} abordan este tema en
+su propuesta de dinero electrónico respaldado. Varias de las soluciones
+anteriores violan las garantías de privacidad para los clientes que
+utilizan estas funciones, y todas, excepto Taler, violan el requisito de
+transparencia de ingresos.
+
+La tercera cuestión importante, a menudo desatendida, es conservar la
+transparencia de los ingresos y, por lo tanto, el cumplimiento del AML y
+KYC. \citet{Fuchsbauer} diseñaron deliberadamente un sistema que
+posibilita la desintermediación para proporcionar una semántica más
+similar al efectivo. Sin embargo, la desintermediación ilimitada
+generalmente no concuerda con las regulaciones del AML y KYC, ya que no
+permite lograr ningún nivel de responsabilidad. Un ejemplo de tal diseño
+es ZCash, un libro mayor distribuido que oculta a la red la información
+sobre el pagador, el beneficiario y el monto de la transacción, siendo
+por lo tanto el sistema de pago perfecto para la delincuencia en línea.
+Solo Taler ofrece tanto la privacidad constante del cliente como la
+transparencia de los ingresos, al mismo tiempo que proporciona un cambio
+eficiente, intercambios atómicos~\cite[consulte][]{Camenisch2007} y la
+capacidad de restaurar billeteras desde una copia de seguridad.
+
+Con respecto a los sistemas de pago para las CBDC, \citet{Danezis} diseñaron
+un libro mayor escalable con RSCoin. Básicamente es un sistema RTGS que es
+protegido utilizando la misma criptografía que se usa en Bitcoin. Al igual que
+Taler, el diseño utiliza la fragmentación de la base de datos para lograr una
+escalabilidad lineal. Sin embargo, el diseño de~\citet{Danezis} no tiene
+ninguna disposición para la privacidad y carece de consideraciones sobre cómo
+integrar prácticamente el diseño con los sistemas y procesos bancarios
+existentes.
+
+La EUROchain del Banco Central Europeo\cite[véase][]{ECB} es otro
+prototipo para CBDC con libro mayor distribuido. Similar a la
+arquitectura propuesta en el presente documento, la EUROchain utiliza
+una arquitectura de dos niveles donde los bancos comerciales actúan como
+intermediarios. Una diferencia crucial es la manera en que los sistemas
+intentan combinar la privacidad y el cumplimiento del AML. En nuestro
+diseño, los reguladores podrían imponer un límite a la cantidad de
+efectivo electrónico que el titular de una cuenta bancaria puede retirar
+durante un cierto tiempo, mientras que la EUROchain emite un número
+limitado de ``vales de anonimato'' que conceden al receptor un número
+limitado de transacciones sin verificación del AML. Como estos vales
+parecen no tener ninguna relación con ningún token de valor, no queda
+claro de qué manera el diseño evitaría la aparición de un mercado negro
+de ``vales de anonimato''. Además, la noción de anonimato de la
+EUROchain es muy diferente, ya que sus ``vales de anonimato'' simplemente
+eliminan ciertas verificaciones del AML, al mismo tiempo que preservan
+la capacidad de los bancos comerciales de ver cómo los consumidores
+gastan el efectivo electrónico. Mientras que los pagadores usuarios de
+Taler interactúan directamente con los comerciantes para gastar su
+efectivo electrónico, el sistema EUROchain requiere que los pagadores
+instruyan a sus bancos comerciales para que accedan a su CBDC. Por lo
+tanto, la EUROchain no emite tokens de valor directamente a los
+consumidores y, en cambio, depende de que los consumidores se
+autentiquen ellos mismos en sus bancos comerciales para acceder a la
+CBDC que el banco central mantiene efectivamente en custodia. Por lo
+tanto, no está claro qué ventajas de privacidad, rendimiento o seguridad
+tiene la EUROchain sobre el dinero existente en depósito.
+
+\section{Conclusión}\label{7.-conclusiuxf3n}
+
+Con la aparición de Bitcoin y monedas digitales recientemente propuestas
+por grandes empresas tecnológicas como Diem (antes Libra), los bancos
+centrales se enfrentan a una competencia cada vez mayor de actores que
+ofrecen su propia alternativa digital al efectivo físico. Las decisiones
+de los bancos centrales sobre la emisión o no de una CBDC dependen de
+cómo evalúen los beneficios y los riesgos de una CBDC. Estos beneficios
+y riesgos, así como las circunstancias jurisdiccionales específicas que
+definen el alcance de las CBDC al por menor, probablemente difieran de
+un país a otro.
+
+Si un banco central decide emitir una CBDC al por menor, proponemos una
+CBDC basada en tokens que combina la privacidad de las transacciones con
+el cumplimiento del KYC, AML y CFT. Dicha CBDC no competiría con los
+depósitos de los bancos comerciales, sino que reproduciría el efectivo
+físico, lo que limitaría los riesgos de estabilidad financiera y
+políticas monetarias.
+
+Hemos demostrado que el esquema propuesto aquí sería tan eficiente y
+rentable como los sistemas RTGS modernos operados por los bancos
+centrales. Los pagos electrónicos con nuestra CBDC solo necesitarían una
+simple base de datos para las transacciones y cantidades minúsculas de
+ancho de banda. La eficiencia y la rentabilidad, junto con la facilidad
+de uso mejorada para el consumidor provocada por el cambio de la
+autenticación a la autorización, hacen que este esquema sea
+probablemente el primero en respaldar el objetivo largamente previsto de
+los micropagos en línea. Además, el uso de monedas para firmar
+criptográficamente contratos electrónicos permitiría el uso de contratos
+inteligentes. Esto también podría conducir a la aparición de
+aplicaciones completamente nuevas para los sistemas de pago. Aunque
+nuestro sistema no se basa en la DLT, podría integrarse fácilmente con
+dichas tecnologías si así lo requirieran las infraestructuras del
+mercado financiero en el futuro.
+
+Igualmente importante, sin embargo, es que una CBDC al por menor debe
+preservar el efectivo como un bien común respetuoso de la privacidad
+bajo el control individual de los ciudadanos. Esto se puede lograr con
+el esquema propuesto en este documento, y los bancos centrales pueden
+evitar perturbaciones significativas en sus políticas monetarias y
+estabilidad financiera cosechando al mismo tiempo los beneficios de la
+digitalización.
+
+
+\newpage
+%REFERENCIAS
+\bibliographystyle{agsm}
+\bibliography{cbdc}
+
+\end{document}
diff --git a/doc/cbdc-es/cbdc.bib b/doc/cbdc-es/cbdc.bib
new file mode 100644
index 00000000..fe0ea626
--- /dev/null
+++ b/doc/cbdc-es/cbdc.bib
@@ -0,0 +1,566 @@
+@article{Adrian,
+ author = {Adrian, Tobias and Tommaso Mancini-Griffoli},
+ year = {2019},
+ title = {The Rise of Digital Money},
+ journal = {IMF Fintech Note},
+ volume = {19/01},
+}
+
+@article{Agarwal,
+ author = {Agarwal, Ruchir and Miles S. Kimball},
+ year = {2019},
+ title = {Enabling Deep Negative Rates to Fight Recessions: A Guide},
+ journal = {IMF Working Paper},
+ volume = {19/84},
+}
+
+
+@article{Agur,
+ author = {Agur, Itai and Anil Ari and Giovanni Dell'Ariccia},
+ year = {2019},
+ title = {Designing Central Bank Digital Currencies},
+ journal = {IMF Working Paper},
+ volume = {19/252},
+}
+
+@article{Allen,
+ author = {Allen, Sarah and Srđjan Čapkun and Ittay Eyal and Giulia Fanti and Bryan A. Ford and James Grimmelmann and Ari Juels and Kari Kostiainen and Sarah Meiklejohn and Andrew Miller and Eswar Prasad and Karl Wüst and Fan Zhang},
+ year = {2020},
+ title = {Design Choices for Central Bank Digital Currency: Policy and Technical Considerations},
+ journal = {NBER Working Paper},
+ volume = {27634},
+}
+
+@article{Alves,
+ author = {Alves, Tiago and Don Felton},
+ year = {2004},
+ title = {TrustZone: Integrated hardware and software security},
+ journal = {ARM IQ},
+ volume = {3},
+ number = {4},
+ pages = {18--24},
+}
+
+@article{AuerBoehme,
+ author = {Auer, Raphael and Rainer Böhme},
+ year = {2020},
+ title = {The technology of retail central bank digital currency},
+ journal = {BIS Quarterly Review},
+ month = {March},
+ pages = {85--96},
+}
+
+@article{AuerCornelli,
+ author = {Auer, Raphael and Giulio Cornelli and Jon Frost},
+ year = {2020},
+ title = {Taking stock: ongoing retail {CBDC} projects},
+ journal = {BIS Quarterly Review},
+ month = {March},
+ pages = {97--98},
+}
+
+@booklet{BIS,
+ author = {{Bank for International Settlements}},
+ year = {2018},
+ title = {Central Bank Digital Currencies. Joint Report of the Committee on Payments and Market Infrastructures and Markets Committee},
+}
+
+@booklet{BoE,
+ author = {{Bank of England}},
+ year = {2020},
+ title = {Central Bank Digital Currency: Opportunities, Challenges and Design. Discussion Paper},
+ month = {March},
+}
+
+@article{Baiocchi,
+ author = {Baiocchi, Giovanni and Walter Distaso},
+ year = {2003},
+ title = {{GRETL}: Econometric Software for the {GNU} Generation},
+ journal = {Journal of Applied Econometrics},
+ volume = {18},
+ pages = {105-110},
+}
+
+@article{Bech,
+ author = {Bech, Morten and Rodney Garratt},
+ year = {2017},
+ title = {Central bank cryptocurrencies},
+ journal = {BIS Quarterly Review},
+ month = {September},
+ pages = {55--70},
+}
+
+@article{Berentsen,
+ author = {Berentsen, Aleksander and Fabian Schär},
+ year = {2018},
+ title = {The Case for Central Bank Electronic Money and the Non-case for Central Bank Cryptocurrencies},
+ journal = {Federal Reserve Bank of St. Louis Review},
+ volume = {100},
+ number = {2},
+ pages = {97--106},
+}
+
+@article{Bernstein2020,
+ author = {Bernstein, Daniel J. and Tanja Lange},
+ year = {2020},
+ title = {{eBACS}: {ECRYPT} Benchmarking of Cryptographic Systems},
+ url = {\url{https://bench.cr.yp.to}, accessed 17 March 2020},
+}
+
+@article{Bernstein2012,
+ author = {Bernstein, Daniel J. and Niels Duif and Tanja Lange and Peter Schwabe and Bo-Yin Yang},
+ year = {2012},
+ title = {High-speed high-security signatures},
+ journal = {Journal of Cryptographic Engineering},
+ volume = {2},
+ pages = {77--89},
+}
+
+@InCollection{Bindseil,
+ author = {Bindseil, Ulrich},
+ year = {2020},
+ title = {Tiered {CBDC} and the financial system},
+ publisher = {European Central Bank},
+ series = {ECB Working Paper},
+ number = {2351},
+ month = {January},
+}
+
+@article{Boar,
+ author = {Boar, Codruta and Henry Holden and Amber Wadsworth},
+ year = {2020},
+ title = {Impending arrival - a sequel to the survey on central bank digital currency},
+ journal = {BIS Papers},
+ volume = {107},
+}
+
+@article{Boneh,
+ author = {Boneh, Dan},
+ year = {1999},
+ title = {Twenty Years of Attacks on the {RSA} Cryptosystem},
+ journal = {Notices of the AMS},
+ volume = {42},
+ number = {2},
+ pages = {202--213},
+}
+
+
+@InCollection{Bordo,
+ author = {Bordo, Michael D. and Andrew T. Levin},
+ year = {2017},
+ title = {Central bank digital currency and the future of monetary policy},
+ publisher = {National Bureau of Economic Research},
+ series = {NBER Working Paper Series},
+ number = {23711},
+}
+
+@article{Brunnermeier,
+ author = {Brunnermeier, Markus and Dirk Niepelt},
+ year = {2019},
+ title = {On the Equivalence of Private and Public Money},
+ journal = {Journal of Monetary Economics},
+ volume = {106},
+ pages = {27--41},
+}
+
+@article{Buiter,
+ author = {Buiter, Willem H. and Nikolaos Panigirtzoglou},
+ year = {2003},
+ title = {Overcoming the Zero Bound on Nominal Interest Rates with Negative Interest on Currency: Gesell's Solution},
+ journal = {The Economic Journal},
+ volume = {113},
+ number = {490},
+ pages = {723--746},
+}
+
+@InCollection{Bullmann,
+ author = {Bullmann, Dirk and Jonas Klemm and Andrea Pinna},
+ year = {2019},
+ title = {In search for stability in crypto-assets: are stablecoins the solution?},
+ publisher = {European Central Bank},
+ series = {ECB Occasional Paper Series},
+ number = {230},
+}
+
+@inproceedings{Camenisch2007,
+ author = {Camenisch, Jan and Aanna Lysyanskaya and Mira Meyerovich},
+ year = {2007},
+ title = {Endorsed E-Cash},
+ booktitle = {2007 IEEE Symposium on Security and Privacy (SP'07)},
+ month = {May},
+ pages = {101--115},
+}
+
+@inproceedings{Camenisch2005,
+ author = {Camenisch, Jan and Susan Hohenberger and Anna Lysyanskaya},
+ year = {2005},
+ title = {Compact E-Cash},
+ booktitle = {Advances in Cryptology -- EUROCRYPT 2005: 24th Annual International Conference on the Theory and Applications of Cryptographic Techniques},
+ address = {Aarhus, Denmark},
+ month = {May},
+ day = {22-26},
+ editor = {Ed. by Ronald Cramer},
+ publisher = {Springer-Verlag Berlin Heidelberg},
+}
+
+
+
+@inproceedings{Canard,
+ author = {Canard, Sébastien and Aline Gouget},
+ year = {2007},
+ title = {Divisible e-cash systems can be truly anonymous},
+ booktitle = {Annual International Conference on the Theory and Applications of Cryptographic Techniques},
+ pages = {482--497},
+}
+
+
+
+@misc{CCC,
+ author = {{CCC e.V.}},
+ year = {2017},
+ title = {Chaos Computer Club hacks e-motor charging stations},
+ howpublished = {34c3},
+}
+
+@article{Chapman,
+ author = {Chapman, James and Rodney Garratt and Scott Hendry and Andrew McCormack and Wade McMahon},
+ year = {2017},
+ title = {Project {J}asper: Are Distributed Wholesale Payment Systems Feasible Yet?},
+ journal = {Financial System Review},
+ publisher = {Bank of Canada},
+ month = {June},
+ pages = {59--69},
+}
+
+@inproceedings{Chaum1983,
+ author = {Chaum, David},
+ year = {1983},
+ title = {Blind signatures for untraceable payments},
+ booktitle = {Advances in Cryptology: Proceedings of Crypto `82},
+ pages = {199--203},
+}
+
+@inproceedings{Chaum1990,
+ author = {Chaum, David and Amos Fiat and Moni Naor},
+ year = {1990},
+ title = {Untraceable electronic cash},
+ booktitle = {Advances in Cryptology: Proceedings of CRYPTO '88},
+ pages = {319--327},
+}
+
+@inproceedings{Danezis,
+ author = {Danezis, George and Sarah Meiklejohn},
+ year = {2016},
+ title = {Centrally Banked Cryptocurrencies},
+ booktitle = {23nd Annual Network and Distributed System Security Symposium, NDSS2016},
+ address = {San Diego, California, USA},
+ month = {February},
+ day = {21--24},
+ publisher = {The Internet Society},
+}
+
+@article{Diffie,
+ author = {Diffie, Whitfield and Martin Hellmann},
+ year = {1976},
+ title = {New Directions in Cryptography},
+ journal = {IEEE Trans. on Inf. Theory, IT-22},
+ pages = {644--654},
+}
+
+@phdthesis{Dold,
+ author = {Dold, Florian},
+ year = {2019},
+ title = {The {GNU} {T}aler System: Practical and Provably Secure Electronic Payments. PhD Thesis},
+ school = {University of Rennes 1},
+}
+
+@article{ECB,
+ author = {{European Central Bank}},
+ year = {2019},
+ title = {Exploring anonymity in central bank digital currencies},
+ journal = {In Focus},
+ number = {4},
+ month = {December},
+}
+
+@inproceedings{Fuchsbauer,
+ author = {Fuchsbauer, Georg and David Pointcheval and Damien Vergnaud},
+ year = {2009},
+ title = {Transferable constant-size fair e-cash},
+ booktitle = {International Conference on Cryptology and Network Security},
+ publisher = {Springer-Verlag Berlin Heidelberg},
+ pages = {226--247},
+}
+
+@inproceedings{Garcia,
+ author = {Garcia, Flavio and Gerhard de Koning Gans and Ruben Muijrers and Peter van Rossum and Roel Verdult and Ronny Wichers Schreur and Bart Jacobs},
+ year = {2008},
+ title = {Dismantling MIFARE Classic},
+ booktitle = {European Symposium on Research in Computer Security},
+}
+
+@article{Garratt,
+ author = {Garratt, Rod and Michael Lee and Brendan Malone and Antoine Martin},
+ year = {2020},
+ title = {Token- or Account-Based? A Digital Currency Can Be Both},
+ journal = {Liberty Street Economics},
+ publisher = {Federal Reserve Bank of New York},
+ month = {August},
+ day = {12},
+}
+
+@article{Goodfriend,
+ author = {Goodfriend, Marvin},
+ year = {2000},
+ title = {Overcoming the Zero Bound on Interest Rate Policy},
+ journal = {Journal of Money, Credit, and Banking},
+ volume = {32},
+ number = {4},
+ pages = {1007--1035},
+}
+
+@article{Johnston,
+ author = {Johnston, Casey},
+ year = {2010},
+ title = {PS3 hacked through poor cryptography implementation},
+ journal = {Ars Technica},
+ month = {December},
+ day = {30},
+}
+
+
+
+@Misc{Jordan,
+ note = {Speech given at the 30th anniversary of the WWZ and VBÖ},
+ author = {Jordan, Thomas J.},
+ year = {2019},
+ title = {Currencies, money and digital tokens},
+ publisher = {University of Basel},
+ month = {September},
+ howpublished = {\url{https://www.snb.ch/en/mmr/speeches/id/ref\_20190905\_tjn/source/ref\_20190905\_tjn.en.pdf}},
+}
+
+
+@article{Kahn2009,
+ author = {Kahn, Charles M. and William Roberds},
+ year = {2009},
+ title = {Why Pay? An Introduction to Payments Economics},
+ journal = {Journal of Financial Intermediation},
+ number = {18},
+ pages = {1--23},
+}
+
+@article{Kahn2005,
+ author = {Kahn, Charles M. and James McAndrews and William Roberds},
+ year = {2005},
+ title = {Money is Privacy},
+ journal = {International Economic Review},
+ volume = {46},
+ number = {2},
+ pages = {377--399},
+}
+
+@article{Kasper,
+ author = {Kasper, Timo and Michael Silbermann and Christof Paar},
+ year = {2010},
+ title = {All you can eat or breaking a real-world contactless payment system},
+ journal = {Financial Cryptography and Data Security, Lecture Notes in Computer Science},
+ volume = {6052},
+ pages = {343--50},
+}
+
+@inproceedings{Katzenbeisser,
+ author = {Katzenbeisser, Stefan and Ünal Kocabaş and Vladimir Rožić and Ahmad-Reza Sadeghi and Ingrid Verbauwhede and Christian Wachsmann},
+ year = {2012},
+ title = {{PUF}s: Myth, Fact or Busted? A Security Evaluation of Physically Unclonable Functions ({PUF}s) Cast in Silicon},
+ booktitle = {Cryptographic Hardware and Embedded Systems -- CHES 2012. Lecture Notes in Computer Science},
+ volume = {7428},
+ pages = {283--301},
+}
+
+@book{Keynes,
+ author = {Keynes, John Maynard},
+ year = {1936},
+ title = {The General Theory of Employment, Interest and Money},
+ publisher = {Macmillan},
+}
+
+@article{Kiff,
+ author = {Kiff, John and Jihad Alwazir and Sonja Davidovic and Aquiles Farias and Ashraf Khan and Tanai Khiaonarong and Majid Malaika and Hunter Monroe and Nobu Sugimoto and Hervé Tourpe and Peter Zhou},
+ year = {2020},
+ title = {A Survey of Research on Retail Central Bank Digital Currency},
+ journal = {IMF Working Paper},
+ volume = {20/104},
+}
+
+@InCollection{Kumhof,
+ author = {Kumhof, Michael and Clare Noone},
+ year = {2018},
+ title = {Central bank digital currencies - design principles and balance sheet implications},
+ publisher = {Bank of England},
+ series = {Staff Working Paper},
+ number = {725},
+}
+
+@inproceedings{Lapid,
+ author = {Lapid, Ben and Avishai Wool},
+ year = {2018},
+ title = {Cache-Attacks on the {ARM} TrustZone Implementations of {AES}-256 and {AES}-256-{GCM} via {GPU}-Based Analysis},
+ booktitle = {International Conference on Selected Areas in Cryptography. Lecture Notes in Computer Science},
+ volume = {11349},
+}
+
+@article{Lerner,
+ author = {Lerner, Josh and Jean Tirole},
+ year = {2005},
+ title = {The Scope of Open Source Licensing},
+ journal = {Journal of Law, Economics \& Organization},
+ volume = {21},
+ pages = {20-56},
+}
+
+@misc{Libra,
+ author = {{Libra Association}},
+ year = {2020},
+ title = {Libra White Paper v2.0},
+ url = {\url{https://libra.org/en-US/white-paper}},
+}
+
+@inproceedings{Lim,
+ author = {Lim, Chae Hoon and Phil Joong Lee},
+ year = {1997},
+ title = {A key recovery attack on discrete log-based schemes using a prime order subgroup},
+ booktitle = {CRYPTO 1997. Lecture Notes in Computer Science},
+ volume = {1294},
+}
+
+@InCollection{Lyons,
+ author = {Lyons, Richard K. and Ganesh Viswanath-Natraj},
+ year = {2020},
+ title = {What Keeps Stablecoins Stable?},
+ publisher = {National Bureau of Economic Research},
+ series = {NBER Working Paper Series},
+ number = {27136},
+ month = {May},
+}
+
+@article{Mancini-Griffoli,
+ author = {Mancini-Griffoli, Tommaso and Maria Soledad Martinez Peria and Itai Agur and Anil Ari and John Kiff and Adina Popescu and Celine Rochon},
+ year = {2018},
+ title = {Casting Light on Central Bank Digital Currency},
+ journal = {IMF Staff Discussion Notes},
+ volume = {18/08},
+ publisher = {International Monetary Fund},
+}
+
+@misc{Nakamoto,
+ author = {Nakamoto, Satoshi},
+ year = {2008},
+ title = {Bitcoin: A Peer-to-Peer Electronic Cash System},
+ url = {\url{https://www.bitcoin.com/bitcoin.pdf}},
+}
+
+@book{Narayanan,
+ author = {Narayanan, Arvind and Joseph Bonneau and Edward Felten and Andrew Miller and Steven Goldfeder},
+ year = {2016},
+ title = {Bitcoin and Cryptocurrency Technologies: A Comprehensive Introduction},
+ publisher = {Princeton University Press},
+}
+
+@misc{Niepelt,
+ author = {Niepelt, Dirk},
+ year = {2020},
+ title = {Digital money and central bank digital currency: An executive summary for policymakers},
+ url = {https://voxeu.org/article/digital-money-and-central-bank-digital-currency-executive-summary},
+}
+
+@inproceedings{Okamoto,
+ author = {Okamoto, Tatsuaki},
+ year = {1995},
+ title = {An Efficient Divisible Electronic Cash Scheme},
+ booktitle = {Advances in Cryptology --- CRYPT0'95: 15th Annual International Cryptology Conference Santa Barbara, California, USA, August 27--31, 1995 Proceedings},
+ editor = {Ed. by Don Coppersmith},
+ publisher = {Springer-Verlag Berlin Heidelberg},
+ pages = {438--451},
+}
+
+@article{Pinto,
+ author = {Pinto, S. and N. Santos},
+ year = {2019},
+ title = {Demystifying {ARM} TrustZone: A Comprehensive Survey},
+ journal = {ACM Computing Surveys},
+ volume = {51},
+ number = {6},
+ month = {January},
+ pages = {1--31}
+}
+
+@article{Rivest,
+ author = {Rivest, Ronald L. and Adi Shamir and Leonard Adleman},
+ year = {1978},
+ title = {A Method for Obtaining Digital Signatures and Public Key Cryptosystems},
+ journal = {Comm. ACM},
+ volume = {21},
+ number = {2},
+}
+
+@book{Solove,
+ author = {Solove, Daniel J.},
+ year = {2011},
+ title = {Nothing to Hide: The false tradeoff between privacy and security},
+ publisher = {New Haven \& London: Yale University Press},
+}
+
+@article{Soukup,
+ author = {Soukup, Michael and Bruno Muff},
+ year = {2007},
+ title = {Die {P}ostcard lässt sich fälschen},
+ journal = {Sonntagszeitung},
+ month = {April},
+ day = {22},
+}
+
+@article{Stallman,
+ author = {Stallman, Richard},
+ year = {1985},
+ title = {The {GNU} manifesto},
+ journal = {Dr. Dobb's Journal of Software Tools},
+ volume = {10},
+ number = {3},
+ pages = {30--35},
+}
+
+
+@TechReport{Riksbank,
+ author = {{Sveriges Riksbank}},
+ year = {2020},
+ title = {The {R}iksbank's e-krona project},
+ month = {Feb},
+ institution = {Sveriges Riksbank},
+ url = {\url{https://www.riksbank.se/globalassets/media/rapporter/e-krona/2019/the-riksbanks-e-krona-pilot.pdf}},
+}
+
+@misc{Wojtczuk,
+ author = {Wojtczuk, Rafal and Joanna Rutkowska},
+ year = {2009},
+ title = {Attacking {I}ntel Trusted Execution Technology},
+ howpublished = {BlackHat-DC 2009},
+}
+
+@article{Yalta2010,
+ author = {Yalta, A. Talha and A. Yasemin Yalta},
+ year = {2010},
+ title = {Should Economists Use Open Source Software for Doing Research?},
+ journal = {Computational Economics},
+ volume = {35},
+ pages = {371--394},
+}
+
+@article{Yalta2008,
+ author = {Yalta, A. Talha and Riccardo Lucchetti},
+ year = {2008},
+ title = {The {GNU/L}inux Platform and Freedom Respecting Software for Economists},
+ journal = {Journal of Applied Econometrics},
+ volume = {23},
+ pages = {279-286},
+}
diff --git a/doc/cbdc-es/deposito.pdf b/doc/cbdc-es/deposito.pdf
new file mode 100644
index 00000000..b798478d
--- /dev/null
+++ b/doc/cbdc-es/deposito.pdf
Binary files differ
diff --git a/doc/cbdc-es/eshyphexh.tex b/doc/cbdc-es/eshyphexh.tex
new file mode 100644
index 00000000..4d4efe02
--- /dev/null
+++ b/doc/cbdc-es/eshyphexh.tex
@@ -0,0 +1,1367 @@
+% Copyright (C) 2019 Javier Bezos, CervanTeX
+% Hyphenation exceptions for Spanish related to the h.
+%
+% Las recientes normas de las Academias de la Lengua establecen la
+% prohibición de partir delante de una hache, salvo cuando se trata de
+% un prefijo productivo. En la practica editorial, dado que la
+% aplicación de este norma puede acarrear dificultades en el ajuste de
+% líneas, se siguen practicando las normas tradicionales, más flexibles.
+% Los nuevos patrones para el español (v. 5.0) se adaptan a lo
+% establecido por las Academias, por lo que se suministra como
+% complemento este archivo de excepciones con los casos más habituales
+% de división ante hache.
+%
+% licence:
+% name: MIT/X11
+% url: https://opensource.org/licenses/MIT
+% text: >
+% Permission is hereby granted, free of charge, to any person obtaining a copy
+% of this software and associated documentation files (the "Software"), to deal
+% in the Software without restriction, including without limitation the rights
+% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+% copies of the Software, and to permit persons to whom the Software is
+% furnished to do so, subject to the following conditions:
+%
+% The above copyright notice and this permission notice shall be included in
+% all copies or substantial portions of the Software.
+%
+% THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+% SOFTWARE.
+% ==========================================
+
+\hyphenation{
+ad-he-re-cer
+ad-he-ren-cia
+ad-he-ren-cias
+ad-he-ren-te
+ad-he-ren-tes
+ad-he-ri-da
+ad-he-ri-das
+ad-he-ri-do
+ad-he-ri-dos
+ad-he-ri-mos
+ad-he-ri-re-mos
+ad-he-ri-ros
+ad-he-ri-rá
+ad-he-ri-rán
+ad-he-ri-rás
+ad-he-ri-ré
+ad-he-ri-réis
+ad-he-ri-ría
+ad-he-ri-ría-mos
+ad-he-ri-ríais
+ad-he-ri-rían
+ad-he-ri-rías
+ad-he-rid
+ad-he-rir
+ad-he-rir-la
+ad-he-rir-las
+ad-he-rir-le
+ad-he-rir-les
+ad-he-rir-lo
+ad-he-rir-los
+ad-he-rir-me
+ad-he-rir-nos
+ad-he-rir-se
+ad-he-rir-te
+ad-he-ris-te
+ad-he-ris-teis
+ad-he-rí
+ad-he-rí-ros-la
+ad-he-rí-ros-las
+ad-he-rí-ros-le
+ad-he-rí-ros-les
+ad-he-rí-ros-lo
+ad-he-rí-ros-los
+ad-he-ría
+ad-he-ría-mos
+ad-he-ríais
+ad-he-rían
+ad-he-rías
+ad-he-rír-me-la
+ad-he-rír-me-las
+ad-he-rír-me-le
+ad-he-rír-me-les
+ad-he-rír-me-lo
+ad-he-rír-me-los
+ad-he-rír-nos-la
+ad-he-rír-nos-las
+ad-he-rír-nos-le
+ad-he-rír-nos-les
+ad-he-rír-nos-lo
+ad-he-rír-nos-los
+ad-he-rír-se-la
+ad-he-rír-se-las
+ad-he-rír-se-le
+ad-he-rír-se-les
+ad-he-rír-se-lo
+ad-he-rír-se-los
+ad-he-rír-te-la
+ad-he-rír-te-las
+ad-he-rír-te-le
+ad-he-rír-te-les
+ad-he-rír-te-lo
+ad-he-rír-te-los
+ad-he-rís
+ad-he-si-va
+ad-he-si-vas
+ad-he-si-vi-da-des
+ad-he-si-vi-dad
+ad-he-si-vo
+ad-he-si-vos
+ad-he-sio-nes
+ad-he-sión
+ad-hi-ra-mos
+ad-hi-rie-ra
+ad-hi-rie-rais
+ad-hi-rie-ran
+ad-hi-rie-ras
+ad-hi-rie-re
+ad-hi-rie-reis
+ad-hi-rie-ren
+ad-hi-rie-res
+ad-hi-rie-ron
+ad-hi-rie-se
+ad-hi-rie-seis
+ad-hi-rie-sen
+ad-hi-rie-ses
+ad-hi-rien-do
+ad-hi-rié-ra-mos
+ad-hi-rié-re-mos
+ad-hi-rié-se-mos
+ad-hi-rién-do-la
+ad-hi-rién-do-las
+ad-hi-rién-do-le
+ad-hi-rién-do-les
+ad-hi-rién-do-lo
+ad-hi-rién-do-los
+ad-hi-rién-do-me
+ad-hi-rién-do-nos
+ad-hi-rién-do-se
+ad-hi-rién-do-te
+ad-hi-rién-doos
+ad-hi-rió
+ad-hi-ráis
+ad-hie-ra
+ad-hie-ran
+ad-hie-ras
+ad-hie-re
+ad-hie-ren
+ad-hie-res
+ad-hie-ro
+ad-hor-tar
+al-ha-ce-na
+al-ha-ce-nas
+al-ha-di-da
+al-ha-di-das
+al-ha-ja
+al-ha-ja-ba
+al-ha-ja-bais
+al-ha-ja-ban
+al-ha-ja-bas
+al-ha-ja-da
+al-ha-ja-das
+al-ha-ja-do
+al-ha-ja-dos
+al-ha-ja-mos
+al-ha-ja-ra
+al-ha-ja-rais
+al-ha-ja-ran
+al-ha-ja-ras
+al-ha-ja-re
+al-ha-ja-re-mos
+al-ha-ja-reis
+al-ha-ja-ren
+al-ha-ja-res
+al-ha-ja-ron
+al-ha-ja-rá
+al-ha-ja-rán
+al-ha-ja-rás
+al-ha-ja-ré
+al-ha-ja-réis
+al-ha-ja-ría
+al-ha-ja-ría-mos
+al-ha-ja-ríais
+al-ha-ja-rían
+al-ha-ja-rías
+al-ha-ja-se
+al-ha-ja-seis
+al-ha-ja-sen
+al-ha-ja-ses
+al-ha-jad
+al-ha-jan
+al-ha-jan-do
+al-ha-jar
+al-ha-jas
+al-ha-jas-te
+al-ha-jas-teis
+al-ha-je
+al-ha-je-mos
+al-ha-je-ra
+al-ha-je-ro
+al-ha-jen
+al-ha-jes
+al-ha-ji-ta
+al-ha-ji-to
+al-ha-jo
+al-ha-jue-la
+al-ha-jue-las
+al-ha-já-ba-mos
+al-ha-já-ra-mos
+al-ha-já-re-mos
+al-ha-já-se-mos
+al-ha-jáis
+al-ha-jé
+al-ha-jéis
+al-ha-jó
+al-ha-jú
+al-ha-mar
+al-ha-me-les
+al-ha-mel
+al-ha-mí
+al-ha-nía
+al-ha-quín
+al-ha-ra-ca
+al-ha-ra-cas
+al-ha-ra-que-ro
+al-ha-ra-quien-ta
+al-ha-ra-quien-tas
+al-ha-ra-quien-to
+al-ha-ra-quien-tos
+al-ha-re-me
+al-ha-va-ra
+al-hai-te
+al-ham-bra
+al-ham-bri-lla
+al-han-da-les
+al-han-dal
+al-har-ma
+al-har-mas
+al-he-lí
+al-he-ña
+al-he-ña-ba
+al-he-ña-bais
+al-he-ña-ban
+al-he-ña-bas
+al-he-ña-da
+al-he-ña-das
+al-he-ña-do
+al-he-ña-dos
+al-he-ña-mos
+al-he-ña-ra
+al-he-ña-rais
+al-he-ña-ran
+al-he-ña-ras
+al-he-ña-re
+al-he-ña-re-mos
+al-he-ña-reis
+al-he-ña-ren
+al-he-ña-res
+al-he-ña-ron
+al-he-ña-rá
+al-he-ña-rán
+al-he-ña-rás
+al-he-ña-ré
+al-he-ña-réis
+al-he-ña-ría
+al-he-ña-ría-mos
+al-he-ña-ríais
+al-he-ña-rían
+al-he-ña-rías
+al-he-ña-se
+al-he-ña-seis
+al-he-ña-sen
+al-he-ña-ses
+al-he-ñad
+al-he-ñan
+al-he-ñan-do
+al-he-ñar
+al-he-ñar-se
+al-he-ñas
+al-he-ñas-te
+al-he-ñas-teis
+al-he-ñe
+al-he-ñe-mos
+al-he-ñen
+al-he-ñes
+al-he-ño
+al-he-ñá-ba-mos
+al-he-ñá-ra-mos
+al-he-ñá-re-mos
+al-he-ñá-se-mos
+al-he-ñáis
+al-he-ñé
+al-he-ñéis
+al-he-ñó
+al-hen-dal
+al-hi-da-da
+al-ho-ja
+al-ho-lí
+al-ho-lía
+al-ho-rra
+al-ho-rre
+al-ho-rro
+al-ho-rría
+al-ho-rí
+al-ho-rín
+al-hol-va
+al-hol-var
+al-hol-vas
+al-hom-bra
+al-hom-brar
+al-hon-di-gue-ro
+al-hor-za
+al-hoz
+al-hu-ce-ma
+al-hu-ce-mas
+al-hu-ce-mi-lla
+al-hu-ce-mi-llas
+al-hu-ce-ña
+al-hu-ma-jo
+al-hu-ma-jos
+al-hu-rre-ca
+al-hu-rre-cas
+al-há-be-ga
+al-há-me-ga
+al-hár-ga-ma
+al-hó-ci-go
+al-hón-di-ga
+al-hón-di-gas
+an-he-do-nia
+an-he-la
+an-he-la-ba
+an-he-la-bais
+an-he-la-ban
+an-he-la-bas
+an-he-la-cio-nes
+an-he-la-ción
+an-he-la-da
+an-he-la-das
+an-he-la-do
+an-he-la-dos
+an-he-la-mos
+an-he-la-ra
+an-he-la-rais
+an-he-la-ran
+an-he-la-ras
+an-he-la-re
+an-he-la-re-mos
+an-he-la-reis
+an-he-la-ren
+an-he-la-res
+an-he-la-ron
+an-he-la-rá
+an-he-la-rán
+an-he-la-rás
+an-he-la-ré
+an-he-la-réis
+an-he-la-ría
+an-he-la-ría-mos
+an-he-la-ríais
+an-he-la-rían
+an-he-la-rías
+an-he-la-se
+an-he-la-seis
+an-he-la-sen
+an-he-la-ses
+an-he-lad
+an-he-lan
+an-he-lan-do
+an-he-lan-te
+an-he-lan-te
+an-he-lan-tes
+an-he-lan-tes
+an-he-lar
+an-he-las
+an-he-las-te
+an-he-las-teis
+an-he-le
+an-he-le-mos
+an-he-len
+an-he-les
+an-he-li-to
+an-he-lo
+an-he-lo-sa
+an-he-lo-sa-men-te
+an-he-lo-sas
+an-he-lo-so
+an-he-lo-sos
+an-he-los
+an-he-lá-ba-mos
+an-he-lá-ra-mos
+an-he-lá-re-mos
+an-he-lá-se-mos
+an-he-láis
+an-he-lé
+an-he-léis
+an-he-ló
+an-hi-dra
+an-hi-dra-sa
+an-hi-dras
+an-hi-dri-do
+an-hi-dri-ta
+an-hi-dri-tas
+an-hi-dro
+an-hi-dro-sis
+an-hi-dros
+an-hé-li-to
+an-hé-li-tos
+an-hí-dri-da
+an-hí-dri-das
+an-hí-dri-do
+an-hí-dri-dos
+apre-hen-da
+apre-hen-da-mos
+apre-hen-dan
+apre-hen-das
+apre-hen-de
+apre-hen-de-mos
+apre-hen-de-re-mos
+apre-hen-de-rá
+apre-hen-de-rán
+apre-hen-de-rás
+apre-hen-de-ré
+apre-hen-de-réis
+apre-hen-de-ría
+apre-hen-de-ría-mos
+apre-hen-de-ríais
+apre-hen-de-rían
+apre-hen-de-rías
+apre-hen-ded
+apre-hen-den
+apre-hen-der
+apre-hen-der-la
+apre-hen-der-las
+apre-hen-der-le
+apre-hen-der-les
+apre-hen-der-lo
+apre-hen-der-los
+apre-hen-des
+apre-hen-di-da
+apre-hen-di-das
+apre-hen-di-do
+apre-hen-di-dos
+apre-hen-di-mos
+apre-hen-die-ra
+apre-hen-die-rais
+apre-hen-die-ran
+apre-hen-die-ras
+apre-hen-die-re
+apre-hen-die-reis
+apre-hen-die-ren
+apre-hen-die-res
+apre-hen-die-ron
+apre-hen-die-se
+apre-hen-die-seis
+apre-hen-die-sen
+apre-hen-die-ses
+apre-hen-dien-do
+apre-hen-dien-te
+apre-hen-dien-tes
+apre-hen-dis-te
+apre-hen-dis-teis
+apre-hen-dié-ra-mos
+apre-hen-dié-re-mos
+apre-hen-dié-se-mos
+apre-hen-dién-do-la
+apre-hen-dién-do-las
+apre-hen-dién-do-le
+apre-hen-dién-do-les
+apre-hen-dién-do-lo
+apre-hen-dién-do-los
+apre-hen-dió
+apre-hen-do
+apre-hen-dáis
+apre-hen-déis
+apre-hen-dí
+apre-hen-día
+apre-hen-día-mos
+apre-hen-díais
+apre-hen-dían
+apre-hen-días
+apre-hen-si-va
+apre-hen-si-vas
+apre-hen-si-vo
+apre-hen-si-vos
+apre-hen-sio-nes
+apre-hen-sión
+apre-hen-so-ra
+apre-hen-so-ras
+apre-hen-so-res
+apre-hen-so-rio
+apre-hen-sor
+ara-hua-co
+chi-hua-hua
+chi-hua-huas
+chi-hua-hue-ño
+chi-hua-huen-se
+co-he-ta-zo
+co-he-te
+co-he-te-ra
+co-he-te-ras
+co-he-te-ro
+co-he-te-ros
+co-he-te-ría
+co-he-tes
+co-hi-ba-mos
+co-hi-bi-cio-nes
+co-hi-bi-ción
+co-hi-bi-da
+co-hi-bi-das
+co-hi-bi-do
+co-hi-bi-dos
+co-hi-bi-mos
+co-hi-bi-ros
+co-hi-bid
+co-hi-bie-ra
+co-hi-bie-rais
+co-hi-bie-ran
+co-hi-bie-ras
+co-hi-bie-re
+co-hi-bie-reis
+co-hi-bie-ren
+co-hi-bie-res
+co-hi-bie-ron
+co-hi-bie-se
+co-hi-bie-seis
+co-hi-bie-sen
+co-hi-bie-ses
+co-hi-bien-do
+co-hi-bir
+co-hi-bir-la
+co-hi-bir-las
+co-hi-bir-le
+co-hi-bir-les
+co-hi-bir-lo
+co-hi-bir-los
+co-hi-bir-me
+co-hi-bir-nos
+co-hi-bir-se
+co-hi-bir-te
+co-hi-bié-ra-mos
+co-hi-bié-re-mos
+co-hi-bié-se-mos
+co-hi-bién-do-la
+co-hi-bién-do-las
+co-hi-bién-do-le
+co-hi-bién-do-les
+co-hi-bién-do-lo
+co-hi-bién-do-los
+co-hi-bién-do-me
+co-hi-bién-do-nos
+co-hi-bién-do-se
+co-hi-bién-do-te
+co-hi-bién-doos
+co-hi-bió
+co-hi-báis
+co-hi-bía
+co-hi-bía-mos
+co-hi-bíais
+co-hi-bían
+co-hi-bías
+co-hi-bís
+dír-ham
+dír-hem
+ex-ha-la
+ex-ha-la-ba
+ex-ha-la-bais
+ex-ha-la-ban
+ex-ha-la-bas
+ex-ha-la-cio-nes
+ex-ha-la-ción
+ex-ha-la-da
+ex-ha-la-das
+ex-ha-la-do
+ex-ha-la-do-ra
+ex-ha-la-do-ras
+ex-ha-la-do-res
+ex-ha-la-dor
+ex-ha-la-dos
+ex-ha-la-mos
+ex-ha-la-ra
+ex-ha-la-rais
+ex-ha-la-ran
+ex-ha-la-ras
+ex-ha-la-re
+ex-ha-la-re-mos
+ex-ha-la-reis
+ex-ha-la-ren
+ex-ha-la-res
+ex-ha-la-ron
+ex-ha-la-ros
+ex-ha-la-rá
+ex-ha-la-rán
+ex-ha-la-rás
+ex-ha-la-ré
+ex-ha-la-réis
+ex-ha-la-ría
+ex-ha-la-ría-mos
+ex-ha-la-ríais
+ex-ha-la-rían
+ex-ha-la-rías
+ex-ha-la-se
+ex-ha-la-seis
+ex-ha-la-sen
+ex-ha-la-ses
+ex-ha-lad
+ex-ha-lan
+ex-ha-lan-do
+ex-ha-lan-te
+ex-ha-lar
+ex-ha-lar-la
+ex-ha-lar-las
+ex-ha-lar-le
+ex-ha-lar-les
+ex-ha-lar-lo
+ex-ha-lar-los
+ex-ha-lar-me
+ex-ha-lar-nos
+ex-ha-lar-se
+ex-ha-lar-te
+ex-ha-las
+ex-ha-las-te
+ex-ha-las-teis
+ex-ha-le
+ex-ha-le-mos
+ex-ha-len
+ex-ha-les
+ex-ha-lo
+ex-ha-lá-ba-mos
+ex-ha-lá-ra-mos
+ex-ha-lá-re-mos
+ex-ha-lá-ros-la
+ex-ha-lá-ros-las
+ex-ha-lá-ros-lo
+ex-ha-lá-ros-los
+ex-ha-lá-se-mos
+ex-ha-láis
+ex-ha-lán-do-la
+ex-ha-lán-do-las
+ex-ha-lán-do-le
+ex-ha-lán-do-les
+ex-ha-lán-do-lo
+ex-ha-lán-do-los
+ex-ha-lán-do-me
+ex-ha-lán-do-me-la
+ex-ha-lán-do-me-las
+ex-ha-lán-do-me-lo
+ex-ha-lán-do-me-los
+ex-ha-lán-do-nos
+ex-ha-lán-do-nos-la
+ex-ha-lán-do-nos-las
+ex-ha-lán-do-nos-lo
+ex-ha-lán-do-nos-los
+ex-ha-lán-do-se
+ex-ha-lán-do-se-la
+ex-ha-lán-do-se-las
+ex-ha-lán-do-se-lo
+ex-ha-lán-do-se-los
+ex-ha-lán-do-te
+ex-ha-lán-do-te-la
+ex-ha-lán-do-te-las
+ex-ha-lán-do-te-lo
+ex-ha-lán-do-te-los
+ex-ha-lán-doos
+ex-ha-lán-doos-la
+ex-ha-lán-doos-las
+ex-ha-lán-doos-lo
+ex-ha-lán-doos-los
+ex-ha-lár-me-la
+ex-ha-lár-me-las
+ex-ha-lár-me-lo
+ex-ha-lár-me-los
+ex-ha-lár-nos-la
+ex-ha-lár-nos-las
+ex-ha-lár-nos-lo
+ex-ha-lár-nos-los
+ex-ha-lár-se-la
+ex-ha-lár-se-las
+ex-ha-lár-se-lo
+ex-ha-lár-se-los
+ex-ha-lár-te-la
+ex-ha-lár-te-las
+ex-ha-lár-te-lo
+ex-ha-lár-te-los
+ex-ha-lé
+ex-ha-léis
+ex-ha-ló
+ex-haus-ta
+ex-haus-ta-ción
+ex-haus-tas
+ex-haus-ti-va
+ex-haus-ti-va-men-te
+ex-haus-ti-vas
+ex-haus-ti-vi-dad
+ex-haus-ti-vo
+ex-haus-ti-vos
+ex-haus-to
+ex-haus-tos
+ex-hi-ba
+ex-hi-ba-mos
+ex-hi-ban
+ex-hi-bas
+ex-hi-be
+ex-hi-ben
+ex-hi-bes
+ex-hi-bi-cio-nes
+ex-hi-bi-cio-nis-mo
+ex-hi-bi-cio-nis-mos
+ex-hi-bi-cio-nis-ta
+ex-hi-bi-cio-nis-tas
+ex-hi-bi-ción
+ex-hi-bi-da
+ex-hi-bi-das
+ex-hi-bi-do
+ex-hi-bi-dor
+ex-hi-bi-dos
+ex-hi-bi-mos
+ex-hi-bi-re-mos
+ex-hi-bi-ros
+ex-hi-bi-rá
+ex-hi-bi-rán
+ex-hi-bi-rás
+ex-hi-bi-ré
+ex-hi-bi-réis
+ex-hi-bi-ría
+ex-hi-bi-ría-mos
+ex-hi-bi-ríais
+ex-hi-bi-rían
+ex-hi-bi-rías
+ex-hi-bid
+ex-hi-bie-ra
+ex-hi-bie-rais
+ex-hi-bie-ran
+ex-hi-bie-ras
+ex-hi-bie-re
+ex-hi-bie-reis
+ex-hi-bie-ren
+ex-hi-bie-res
+ex-hi-bie-ron
+ex-hi-bie-se
+ex-hi-bie-seis
+ex-hi-bie-sen
+ex-hi-bie-ses
+ex-hi-bien-do
+ex-hi-bir
+ex-hi-bir-la
+ex-hi-bir-las
+ex-hi-bir-le
+ex-hi-bir-les
+ex-hi-bir-lo
+ex-hi-bir-los
+ex-hi-bir-me
+ex-hi-bir-nos
+ex-hi-bir-se
+ex-hi-bir-te
+ex-hi-bis-te
+ex-hi-bis-teis
+ex-hi-bié-ra-mos
+ex-hi-bié-re-mos
+ex-hi-bié-se-mos
+ex-hi-bién-do-la
+ex-hi-bién-do-las
+ex-hi-bién-do-le
+ex-hi-bién-do-les
+ex-hi-bién-do-lo
+ex-hi-bién-do-los
+ex-hi-bién-do-me
+ex-hi-bién-do-me-la
+ex-hi-bién-do-me-las
+ex-hi-bién-do-me-lo
+ex-hi-bién-do-me-los
+ex-hi-bién-do-nos
+ex-hi-bién-do-nos-la
+ex-hi-bién-do-nos-las
+ex-hi-bién-do-nos-lo
+ex-hi-bién-do-nos-los
+ex-hi-bién-do-se
+ex-hi-bién-do-se-la
+ex-hi-bién-do-se-las
+ex-hi-bién-do-se-lo
+ex-hi-bién-do-se-los
+ex-hi-bién-do-te
+ex-hi-bién-do-te-la
+ex-hi-bién-do-te-las
+ex-hi-bién-do-te-lo
+ex-hi-bién-do-te-los
+ex-hi-bién-doos
+ex-hi-bién-doos-la
+ex-hi-bién-doos-las
+ex-hi-bién-doos-lo
+ex-hi-bién-doos-los
+ex-hi-bió
+ex-hi-bo
+ex-hi-báis
+ex-hi-bí
+ex-hi-bí-ros-la
+ex-hi-bí-ros-las
+ex-hi-bí-ros-lo
+ex-hi-bí-ros-los
+ex-hi-bía
+ex-hi-bía-mos
+ex-hi-bíais
+ex-hi-bían
+ex-hi-bías
+ex-hi-bír-me-la
+ex-hi-bír-me-las
+ex-hi-bír-me-lo
+ex-hi-bír-me-los
+ex-hi-bír-nos-la
+ex-hi-bír-nos-las
+ex-hi-bír-nos-lo
+ex-hi-bír-nos-los
+ex-hi-bír-se-la
+ex-hi-bír-se-las
+ex-hi-bír-se-lo
+ex-hi-bír-se-los
+ex-hi-bír-te-la
+ex-hi-bír-te-las
+ex-hi-bír-te-lo
+ex-hi-bír-te-los
+ex-hi-bís
+ex-hor-ta
+ex-hor-ta-ba
+ex-hor-ta-bais
+ex-hor-ta-ban
+ex-hor-ta-bas
+ex-hor-ta-cio-nes
+ex-hor-ta-ción
+ex-hor-ta-da
+ex-hor-ta-das
+ex-hor-ta-do
+ex-hor-ta-do-ra
+ex-hor-ta-do-ras
+ex-hor-ta-do-res
+ex-hor-ta-dor
+ex-hor-ta-dos
+ex-hor-ta-mos
+ex-hor-ta-ra
+ex-hor-ta-rais
+ex-hor-ta-ran
+ex-hor-ta-ras
+ex-hor-ta-re
+ex-hor-ta-re-mos
+ex-hor-ta-reis
+ex-hor-ta-ren
+ex-hor-ta-res
+ex-hor-ta-ron
+ex-hor-ta-ros
+ex-hor-ta-rá
+ex-hor-ta-rán
+ex-hor-ta-rás
+ex-hor-ta-ré
+ex-hor-ta-réis
+ex-hor-ta-ría
+ex-hor-ta-ría-mos
+ex-hor-ta-ríais
+ex-hor-ta-rían
+ex-hor-ta-rías
+ex-hor-ta-se
+ex-hor-ta-seis
+ex-hor-ta-sen
+ex-hor-ta-ses
+ex-hor-ta-ti-va
+ex-hor-ta-ti-va-men-te
+ex-hor-ta-ti-vas
+ex-hor-ta-ti-vo
+ex-hor-ta-ti-vos
+ex-hor-ta-to-ria
+ex-hor-ta-to-rias
+ex-hor-ta-to-rio
+ex-hor-ta-to-rios
+ex-hor-tad
+ex-hor-tan
+ex-hor-tan-do
+ex-hor-tar
+ex-hor-tar-la
+ex-hor-tar-las
+ex-hor-tar-le
+ex-hor-tar-les
+ex-hor-tar-lo
+ex-hor-tar-los
+ex-hor-tar-me
+ex-hor-tar-nos
+ex-hor-tar-se
+ex-hor-tar-te
+ex-hor-tas
+ex-hor-tas-te
+ex-hor-tas-teis
+ex-hor-te
+ex-hor-te-mos
+ex-hor-ten
+ex-hor-tes
+ex-hor-to
+ex-hor-tos
+ex-hor-tá-ba-mos
+ex-hor-tá-ra-mos
+ex-hor-tá-re-mos
+ex-hor-tá-ros-la
+ex-hor-tá-ros-las
+ex-hor-tá-ros-lo
+ex-hor-tá-ros-los
+ex-hor-tá-se-mos
+ex-hor-táis
+ex-hor-tán-do-la
+ex-hor-tán-do-las
+ex-hor-tán-do-le
+ex-hor-tán-do-les
+ex-hor-tán-do-lo
+ex-hor-tán-do-los
+ex-hor-tán-do-me
+ex-hor-tán-do-me-la
+ex-hor-tán-do-me-las
+ex-hor-tán-do-me-lo
+ex-hor-tán-do-me-los
+ex-hor-tán-do-nos
+ex-hor-tán-do-nos-la
+ex-hor-tán-do-nos-las
+ex-hor-tán-do-nos-lo
+ex-hor-tán-do-nos-los
+ex-hor-tán-do-se
+ex-hor-tán-do-se-la
+ex-hor-tán-do-se-las
+ex-hor-tán-do-se-lo
+ex-hor-tán-do-se-los
+ex-hor-tán-do-te
+ex-hor-tán-do-te-la
+ex-hor-tán-do-te-las
+ex-hor-tán-do-te-lo
+ex-hor-tán-do-te-los
+ex-hor-tán-doos
+ex-hor-tán-doos-la
+ex-hor-tán-doos-las
+ex-hor-tán-doos-lo
+ex-hor-tán-doos-los
+ex-hor-tár-me-la
+ex-hor-tár-me-las
+ex-hor-tár-me-lo
+ex-hor-tár-me-los
+ex-hor-tár-nos-la
+ex-hor-tár-nos-las
+ex-hor-tár-nos-lo
+ex-hor-tár-nos-los
+ex-hor-tár-se-la
+ex-hor-tár-se-las
+ex-hor-tár-se-lo
+ex-hor-tár-se-los
+ex-hor-tár-te-la
+ex-hor-tár-te-las
+ex-hor-tár-te-lo
+ex-hor-tár-te-los
+ex-hor-té
+ex-hor-téis
+ex-hor-tó
+ex-hu-ma
+ex-hu-ma-ba
+ex-hu-ma-bais
+ex-hu-ma-ban
+ex-hu-ma-bas
+ex-hu-ma-cio-nes
+ex-hu-ma-ción
+ex-hu-ma-da
+ex-hu-ma-das
+ex-hu-ma-do
+ex-hu-ma-do-ra
+ex-hu-ma-do-ras
+ex-hu-ma-do-res
+ex-hu-ma-dor
+ex-hu-ma-dos
+ex-hu-ma-mos
+ex-hu-ma-ra
+ex-hu-ma-rais
+ex-hu-ma-ran
+ex-hu-ma-ras
+ex-hu-ma-re
+ex-hu-ma-re-mos
+ex-hu-ma-reis
+ex-hu-ma-ren
+ex-hu-ma-res
+ex-hu-ma-ron
+ex-hu-ma-ros
+ex-hu-ma-rá
+ex-hu-ma-rán
+ex-hu-ma-rás
+ex-hu-ma-ré
+ex-hu-ma-réis
+ex-hu-ma-ría
+ex-hu-ma-ría-mos
+ex-hu-ma-ríais
+ex-hu-ma-rían
+ex-hu-ma-rías
+ex-hu-ma-se
+ex-hu-ma-seis
+ex-hu-ma-sen
+ex-hu-ma-ses
+ex-hu-mad
+ex-hu-man
+ex-hu-man-do
+ex-hu-mar
+ex-hu-mar-la
+ex-hu-mar-las
+ex-hu-mar-le
+ex-hu-mar-les
+ex-hu-mar-lo
+ex-hu-mar-los
+ex-hu-mar-me
+ex-hu-mar-nos
+ex-hu-mar-se
+ex-hu-mar-te
+ex-hu-mas
+ex-hu-mas-te
+ex-hu-mas-teis
+ex-hu-me
+ex-hu-me-mos
+ex-hu-men
+ex-hu-mes
+ex-hu-mo
+ex-hu-má-ba-mos
+ex-hu-má-ra-mos
+ex-hu-má-re-mos
+ex-hu-má-se-mos
+ex-hu-máis
+ex-hu-mán-do-la
+ex-hu-mán-do-las
+ex-hu-mán-do-le
+ex-hu-mán-do-les
+ex-hu-mán-do-lo
+ex-hu-mán-do-los
+ex-hu-mán-do-me
+ex-hu-mán-do-nos
+ex-hu-mán-do-se
+ex-hu-mán-do-te
+ex-hu-mán-doos
+ex-hu-mé
+ex-hu-méis
+ex-hu-mó
+fluor-hí-dri-co
+fluor-hí-dri-cos
+in-ha-la
+in-ha-la-ba
+in-ha-la-bais
+in-ha-la-ban
+in-ha-la-bas
+in-ha-la-ble
+in-ha-la-cio-nes
+in-ha-la-ción
+in-ha-la-da
+in-ha-la-das
+in-ha-la-do
+in-ha-la-do-ra
+in-ha-la-do-ras
+in-ha-la-do-res
+in-ha-la-dor
+in-ha-la-dos
+in-ha-la-mos
+in-ha-la-ra
+in-ha-la-rais
+in-ha-la-ran
+in-ha-la-ras
+in-ha-la-re
+in-ha-la-re-mos
+in-ha-la-reis
+in-ha-la-ren
+in-ha-la-res
+in-ha-la-ron
+in-ha-la-rá
+in-ha-la-rán
+in-ha-la-rás
+in-ha-la-ré
+in-ha-la-réis
+in-ha-la-ría
+in-ha-la-ría-mos
+in-ha-la-ríais
+in-ha-la-rían
+in-ha-la-rías
+in-ha-la-se
+in-ha-la-seis
+in-ha-la-sen
+in-ha-la-ses
+in-ha-lad
+in-ha-lan
+in-ha-lan-do
+in-ha-lan-te
+in-ha-lar
+in-ha-lar-la
+in-ha-lar-las
+in-ha-lar-le
+in-ha-lar-les
+in-ha-lar-lo
+in-ha-lar-los
+in-ha-las
+in-ha-las-te
+in-ha-las-teis
+in-ha-le
+in-ha-le-mos
+in-ha-len
+in-ha-les
+in-ha-lla-ble
+in-ha-lo
+in-ha-lá-ba-mos
+in-ha-lá-ra-mos
+in-ha-lá-re-mos
+in-ha-lá-se-mos
+in-ha-láis
+in-ha-lán-do-la
+in-ha-lán-do-las
+in-ha-lán-do-le
+in-ha-lán-do-les
+in-ha-lán-do-lo
+in-ha-lán-do-los
+in-ha-lé
+in-ha-léis
+in-ha-ló
+in-he-ren-te
+in-he-ren-te-men-te
+in-he-ren-tes
+in-he-sión
+in-hes-ta
+in-hes-ta-ba
+in-hes-ta-bais
+in-hes-ta-ban
+in-hes-ta-bas
+in-hes-ta-da
+in-hes-ta-das
+in-hes-ta-do
+in-hes-ta-dos
+in-hes-ta-mos
+in-hes-ta-ra
+in-hes-ta-rais
+in-hes-ta-ran
+in-hes-ta-ras
+in-hes-ta-re
+in-hes-ta-re-mos
+in-hes-ta-reis
+in-hes-ta-ren
+in-hes-ta-res
+in-hes-ta-ron
+in-hes-ta-rá
+in-hes-ta-rán
+in-hes-ta-rás
+in-hes-ta-ré
+in-hes-ta-réis
+in-hes-ta-ría
+in-hes-ta-ría-mos
+in-hes-ta-ríais
+in-hes-ta-rían
+in-hes-ta-rías
+in-hes-ta-se
+in-hes-ta-seis
+in-hes-ta-sen
+in-hes-ta-ses
+in-hes-tad
+in-hes-tan
+in-hes-tan-do
+in-hes-tar
+in-hes-tar-la
+in-hes-tar-las
+in-hes-tar-le
+in-hes-tar-les
+in-hes-tar-lo
+in-hes-tar-los
+in-hes-tas
+in-hes-tas-te
+in-hes-tas-teis
+in-hes-te
+in-hes-te-mos
+in-hes-ten
+in-hes-tes
+in-hes-to
+in-hes-tá-ba-mos
+in-hes-tá-ra-mos
+in-hes-tá-re-mos
+in-hes-tá-se-mos
+in-hes-táis
+in-hes-tán-do-la
+in-hes-tán-do-las
+in-hes-tán-do-le
+in-hes-tán-do-les
+in-hes-tán-do-lo
+in-hes-tán-do-los
+in-hes-té
+in-hes-téis
+in-hes-tó
+in-hi-ba
+in-hi-ba-mos
+in-hi-ban
+in-hi-bas
+in-hi-be
+in-hi-ben
+in-hi-bes
+in-hi-bi-cio-nes
+in-hi-bi-ción
+in-hi-bi-da
+in-hi-bi-das
+in-hi-bi-do
+in-hi-bi-do-ra
+in-hi-bi-do-ra
+in-hi-bi-do-ras
+in-hi-bi-dor
+in-hi-bi-dos
+in-hi-bi-mos
+in-hi-bi-re-mos
+in-hi-bi-ros
+in-hi-bi-rá
+in-hi-bi-rán
+in-hi-bi-rás
+in-hi-bi-ré
+in-hi-bi-réis
+in-hi-bi-ría
+in-hi-bi-ría-mos
+in-hi-bi-ríais
+in-hi-bi-rían
+in-hi-bi-rías
+in-hi-bi-to-ria
+in-hi-bi-to-rio
+in-hi-bid
+in-hi-bie-ra
+in-hi-bie-rais
+in-hi-bie-ran
+in-hi-bie-ras
+in-hi-bie-re
+in-hi-bie-reis
+in-hi-bie-ren
+in-hi-bie-res
+in-hi-bie-ron
+in-hi-bie-se
+in-hi-bie-seis
+in-hi-bie-sen
+in-hi-bie-ses
+in-hi-bien-do
+in-hi-bir
+in-hi-bir-la
+in-hi-bir-las
+in-hi-bir-le
+in-hi-bir-les
+in-hi-bir-lo
+in-hi-bir-los
+in-hi-bir-me
+in-hi-bir-nos
+in-hi-bir-se
+in-hi-bir-te
+in-hi-bis-te
+in-hi-bis-teis
+in-hi-bié-ra-mos
+in-hi-bié-re-mos
+in-hi-bié-se-mos
+in-hi-bién-do-la
+in-hi-bién-do-las
+in-hi-bién-do-le
+in-hi-bién-do-les
+in-hi-bién-do-lo
+in-hi-bién-do-los
+in-hi-bién-do-me
+in-hi-bién-do-nos
+in-hi-bién-do-se
+in-hi-bién-do-te
+in-hi-bién-doos
+in-hi-bió
+in-hi-bo
+in-hi-báis
+in-hi-bí
+in-hi-bía
+in-hi-bía-mos
+in-hi-bíais
+in-hi-bían
+in-hi-bías
+in-hi-bís
+in-hies-ta
+in-hies-tas
+in-hies-to
+in-hies-tos
+in-hu-ma
+in-hu-ma-ba
+in-hu-ma-bais
+in-hu-ma-ban
+in-hu-ma-bas
+in-hu-ma-cio-nes
+in-hu-ma-ción
+in-hu-ma-da
+in-hu-ma-das
+in-hu-ma-do
+in-hu-ma-dos
+in-hu-ma-mos
+in-hu-ma-ni-da-des
+in-hu-ma-ni-dad
+in-hu-ma-ra
+in-hu-ma-rais
+in-hu-ma-ran
+in-hu-ma-ras
+in-hu-ma-re
+in-hu-ma-re-mos
+in-hu-ma-reis
+in-hu-ma-ren
+in-hu-ma-res
+in-hu-ma-ron
+in-hu-ma-rá
+in-hu-ma-rán
+in-hu-ma-rás
+in-hu-ma-ré
+in-hu-ma-réis
+in-hu-ma-ría
+in-hu-ma-ría-mos
+in-hu-ma-ríais
+in-hu-ma-rían
+in-hu-ma-rías
+in-hu-ma-se
+in-hu-ma-seis
+in-hu-ma-sen
+in-hu-ma-ses
+in-hu-mad
+in-hu-man
+in-hu-man-do
+in-hu-mar
+in-hu-mar-la
+in-hu-mar-las
+in-hu-mar-le
+in-hu-mar-les
+in-hu-mar-lo
+in-hu-mar-los
+in-hu-mas
+in-hu-mas-te
+in-hu-mas-teis
+in-hu-me
+in-hu-me-mos
+in-hu-men
+in-hu-mes
+in-hu-mo
+in-hu-má-ba-mos
+in-hu-má-ra-mos
+in-hu-má-re-mos
+in-hu-má-se-mos
+in-hu-máis
+in-hu-mán-do-la
+in-hu-mán-do-las
+in-hu-mán-do-le
+in-hu-mán-do-les
+in-hu-mán-do-lo
+in-hu-mán-do-los
+in-hu-mé
+in-hu-méis
+in-hu-mó
+in-hós-pi-ta
+in-hós-pi-tas
+in-hós-pi-to
+in-hós-pi-tos
+ma-ri-hua-na
+ma-ri-hua-nas
+ma-ri-hua-ne-ro
+men-hi-res
+men-hir
+pa-ri-hue-la
+pa-ri-hue-las
+sa-bi-hon-da
+sa-bi-hon-das
+sa-bi-hon-de-ces
+sa-bi-hon-dez
+sa-bi-hon-do
+sa-bi-hon-dos
+sa-ha-raui
+sa-ha-ria-na
+sa-ha-ria-nas
+sa-ha-ria-nos
+sa-ha-riano} \ No newline at end of file
diff --git a/doc/cbdc-es/graphic-es.odp b/doc/cbdc-es/graphic-es.odp
new file mode 100644
index 00000000..818c2b18
--- /dev/null
+++ b/doc/cbdc-es/graphic-es.odp
Binary files differ
diff --git a/doc/cbdc-es/retirada.pdf b/doc/cbdc-es/retirada.pdf
new file mode 100644
index 00000000..7efc1549
--- /dev/null
+++ b/doc/cbdc-es/retirada.pdf
Binary files differ
diff --git a/doc/cbdc-es/taler_figure_1_dora_SPANISH.jpg b/doc/cbdc-es/taler_figure_1_dora_SPANISH.jpg
new file mode 100644
index 00000000..0dfd64ae
--- /dev/null
+++ b/doc/cbdc-es/taler_figure_1_dora_SPANISH.jpg
Binary files differ
diff --git a/doc/cbdc-es/taler_figure_2_dora_SPANISH.jpg b/doc/cbdc-es/taler_figure_2_dora_SPANISH.jpg
new file mode 100644
index 00000000..75f476a9
--- /dev/null
+++ b/doc/cbdc-es/taler_figure_2_dora_SPANISH.jpg
Binary files differ
diff --git a/src/auditor/Makefile.am b/src/auditor/Makefile.am
index 09a0deec..b876805b 100644
--- a/src/auditor/Makefile.am
+++ b/src/auditor/Makefile.am
@@ -196,7 +196,8 @@ check_SCRIPTS = \
test-sync.sh
.NOTPARALLEL:
-TESTS = $(check_SCRIPTS)
+# Disabled for now: need working wallet first!
+# TESTS = $(check_SCRIPTS)
EXTRA_DIST = \
taler-auditor.in \
diff --git a/src/auditor/report-lib.c b/src/auditor/report-lib.c
index 4addce9e..e6468f69 100644
--- a/src/auditor/report-lib.c
+++ b/src/auditor/report-lib.c
@@ -141,7 +141,7 @@ add_denomination (
(void) denom_pub;
if (NULL !=
GNUNET_CONTAINER_multihashmap_get (denominations,
- &issue->denom_hash))
+ &issue->denom_hash.hash))
return; /* value already known */
#if GNUNET_EXTRA_LOGGING >= 1
{
@@ -151,7 +151,7 @@ add_denomination (
&issue->value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Tracking denomination `%s' (%s)\n",
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (&value));
TALER_amount_ntoh (&value,
&issue->fee_withdraw);
@@ -176,7 +176,7 @@ add_denomination (
i->master = TALER_ARL_master_pub;
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (denominations,
- &issue->denom_hash,
+ &issue->denom_hash.hash,
i,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@@ -193,7 +193,7 @@ add_denomination (
*/
enum GNUNET_DB_QueryStatus
TALER_ARL_get_denomination_info_by_hash (
- const struct GNUNET_HashCode *dh,
+ const struct TALER_DenominationHash *dh,
const struct TALER_DenominationKeyValidityPS **issue)
{
enum GNUNET_DB_QueryStatus qs;
@@ -215,7 +215,7 @@ TALER_ARL_get_denomination_info_by_hash (
const struct TALER_DenominationKeyValidityPS *i;
i = GNUNET_CONTAINER_multihashmap_get (denominations,
- dh);
+ &dh->hash);
if (NULL != i)
{
/* cache hit */
@@ -246,7 +246,7 @@ TALER_ARL_get_denomination_info_by_hash (
const struct TALER_DenominationKeyValidityPS *i;
i = GNUNET_CONTAINER_multihashmap_get (denominations,
- dh);
+ &dh->hash);
if (NULL != i)
{
/* cache hit */
@@ -275,14 +275,14 @@ enum GNUNET_DB_QueryStatus
TALER_ARL_get_denomination_info (
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_DenominationKeyValidityPS **issue,
- struct GNUNET_HashCode *dh)
+ struct TALER_DenominationHash *dh)
{
- struct GNUNET_HashCode hc;
+ struct TALER_DenominationHash hc;
if (NULL == dh)
dh = &hc;
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
- dh);
+ TALER_denom_pub_hash (denom_pub,
+ dh);
return TALER_ARL_get_denomination_info_by_hash (dh,
issue);
}
diff --git a/src/auditor/report-lib.h b/src/auditor/report-lib.h
index 7c01b168..3f448976 100644
--- a/src/auditor/report-lib.h
+++ b/src/auditor/report-lib.h
@@ -101,7 +101,7 @@ TALER_ARL_report (json_t *array,
*/
enum GNUNET_DB_QueryStatus
TALER_ARL_get_denomination_info_by_hash (
- const struct GNUNET_HashCode *dh,
+ const struct TALER_DenominationHash *dh,
const struct TALER_DenominationKeyValidityPS **issue);
@@ -118,7 +118,7 @@ enum GNUNET_DB_QueryStatus
TALER_ARL_get_denomination_info (
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_DenominationKeyValidityPS **issue,
- struct GNUNET_HashCode *dh);
+ struct TALER_DenominationHash *dh);
/**
diff --git a/src/auditor/taler-auditor-dbinit.c b/src/auditor/taler-auditor-dbinit.c
index ab97b739..54f8152a 100644
--- a/src/auditor/taler-auditor-dbinit.c
+++ b/src/auditor/taler-auditor-dbinit.c
@@ -21,6 +21,7 @@
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
#include "taler_auditordb_plugin.h"
diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c
index 47d61866..a212eddc 100644
--- a/src/auditor/taler-auditor-httpd.c
+++ b/src/auditor/taler-auditor-httpd.c
@@ -410,6 +410,9 @@ run (void *cls,
enum TALER_MHD_GlobalOptions go;
int fh;
+ (void) cls;
+ (void) args;
+ (void) cfgfile;
go = TALER_MHD_GO_NONE;
if (auditor_connection_close)
go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.c b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
index c7a23d71..8f16f36f 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.c
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
@@ -183,33 +183,25 @@ verify_and_execute_deposit_confirmation (
}
/* check deposit confirmation signature */
+ if (GNUNET_OK !=
+ TALER_exchange_deposit_confirm_verify (&dc->h_contract_terms,
+ &dc->h_wire,
+ NULL /* h_extensions! */,
+ dc->exchange_timestamp,
+ dc->wire_deadline,
+ dc->refund_deadline,
+ &dc->amount_without_fee,
+ &dc->coin_pub,
+ &dc->merchant,
+ &dc->exchange_pub,
+ &dc->exchange_sig))
{
- struct TALER_DepositConfirmationPS dcs = {
- .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT),
- .purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)),
- .h_contract_terms = dc->h_contract_terms,
- .h_wire = dc->h_wire,
- .exchange_timestamp = GNUNET_TIME_absolute_hton (dc->exchange_timestamp),
- .refund_deadline = GNUNET_TIME_absolute_hton (dc->refund_deadline),
- .coin_pub = dc->coin_pub,
- .merchant = dc->merchant
- };
-
- TALER_amount_hton (&dcs.amount_without_fee,
- &dc->amount_without_fee);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT,
- &dcs,
- &dc->exchange_sig.eddsa_signature,
- &dc->exchange_pub.eddsa_pub))
- {
- TALER_LOG_WARNING (
- "Invalid signature on /deposit-confirmation request\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_AUDITOR_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID,
- "exchange signature invalid");
- }
+ TALER_LOG_WARNING (
+ "Invalid signature on /deposit-confirmation request\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_AUDITOR_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID,
+ "exchange signature invalid");
}
/* execute transaction */
@@ -257,12 +249,16 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&dc.h_contract_terms),
+ GNUNET_JSON_spec_fixed_auto ("h_extensions",
+ &dc.h_extensions),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&dc.h_wire),
TALER_JSON_spec_absolute_time ("exchange_timestamp",
&dc.exchange_timestamp),
TALER_JSON_spec_absolute_time ("refund_deadline",
&dc.refund_deadline),
+ TALER_JSON_spec_absolute_time ("wire_deadline",
+ &dc.wire_deadline),
TALER_JSON_spec_amount ("amount_without_fee",
TAH_currency,
&dc.amount_without_fee),
diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c
index 0dfaa19a..17c4da46 100644
--- a/src/auditor/taler-auditor-sync.c
+++ b/src/auditor/taler-auditor-sync.c
@@ -310,6 +310,7 @@ do_sync (void *cls)
{
static struct GNUNET_TIME_Relative delay;
+ (void) cls;
sync_task = NULL;
actual_size = 0;
if (GNUNET_SYSERR ==
@@ -469,6 +470,7 @@ load_config (const char *cfgfile)
static void
do_shutdown (void *cls)
{
+ (void) cls;
if (NULL != sync_task)
{
GNUNET_SCHEDULER_cancel (sync_task);
diff --git a/src/auditor/taler-helper-auditor-aggregation.c b/src/auditor/taler-helper-auditor-aggregation.c
index 468f66b4..d4ad08aa 100644
--- a/src/auditor/taler-helper-auditor-aggregation.c
+++ b/src/auditor/taler-helper-auditor-aggregation.c
@@ -358,9 +358,9 @@ struct WireCheckContext
struct TALER_Amount total_deposits;
/**
- * Hash of the wire transfer details of the receiver.
+ * Target account details of the receiver.
*/
- struct GNUNET_HashCode h_wire;
+ const char *payto_uri;
/**
* Execution time of the wire transfer.
@@ -393,7 +393,7 @@ struct WireCheckContext
static int
check_transaction_history_for_deposit (
const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct GNUNET_HashCode *h_contract_terms,
+ const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_DenominationKeyValidityPS *issue,
const struct TALER_EXCHANGEDB_TransactionList *tl_head,
@@ -440,27 +440,6 @@ check_transaction_history_for_deposit (
{
case TALER_EXCHANGEDB_TT_DEPOSIT:
/* check wire and h_wire are consistent */
- {
- struct GNUNET_HashCode hw;
-
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (
- tl->details.deposit->receiver_wire_account,
- &hw))
- {
- report_row_inconsistency ("deposits",
- tl->serial_id,
- "wire account given is malformed");
- }
- else if (0 !=
- GNUNET_memcmp (&hw,
- &tl->details.deposit->h_wire))
- {
- report_row_inconsistency ("deposits",
- tl->serial_id,
- "h(wire) does not match wire");
- }
- }
amount_with_fee = &tl->details.deposit->amount_with_fee; /* according to exchange*/
fee_claimed = &tl->details.deposit->deposit_fee; /* Fee according to exchange DB */
TALER_ARL_amount_add (&expenditures,
@@ -691,7 +670,7 @@ check_transaction_history_for_deposit (
"Coin %s contributes %s to contract %s\n",
TALER_B2S (coin_pub),
TALER_amount2s (merchant_gain),
- GNUNET_h2s (h_contract_terms));
+ GNUNET_h2s (&h_contract_terms->hash));
return GNUNET_OK;
}
@@ -703,8 +682,7 @@ check_transaction_history_for_deposit (
* @param[in,out] cls a `struct WireCheckContext`
* @param rowid which row in the table 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 h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
- * @param account_details where did we transfer the funds?
+ * @param account_pay_uri where did we transfer the funds?
* @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 denom_pub denomination of @a coin_pub
@@ -719,10 +697,9 @@ wire_transfer_information_cb (
void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode *h_wire,
- const json_t *account_details,
+ const char *account_pay_uri,
struct GNUNET_TIME_Absolute exec_time,
- const struct GNUNET_HashCode *h_contract_terms,
+ const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
@@ -735,24 +712,6 @@ wire_transfer_information_cb (
struct TALER_EXCHANGEDB_TransactionList *tl;
struct TALER_CoinPublicInfo coin;
enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_HashCode hw;
-
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (account_details,
- &hw))
- {
- report_row_inconsistency ("aggregation",
- rowid,
- "failed to compute hash of given wire data");
- }
- else if (0 !=
- GNUNET_memcmp (&hw,
- h_wire))
- {
- report_row_inconsistency ("aggregation",
- rowid,
- "database contains wrong hash code for wire details");
- }
/* Obtain coin's transaction history */
qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
@@ -788,7 +747,7 @@ wire_transfer_information_cb (
&issue);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
- GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
+ TALER_denom_sig_free (&coin.denom_sig);
TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
tl);
if (0 == qs)
@@ -819,7 +778,7 @@ wire_transfer_information_cb (
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
coin_value);
- GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
+ TALER_denom_sig_free (&coin.denom_sig);
TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
tl);
report_row_inconsistency ("deposit",
@@ -827,8 +786,7 @@ wire_transfer_information_cb (
"coin denomination signature invalid");
return;
}
- GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
- coin.denom_sig.rsa_signature = NULL; /* just to be sure */
+ TALER_denom_sig_free (&coin.denom_sig);
GNUNET_assert (NULL != issue); /* mostly to help static analysis */
/* Check transaction history to see if it supports aggregate
valuation */
@@ -879,14 +837,13 @@ wire_transfer_information_cb (
"aggregation (contribution)",
rowid,
&coin_value_without_fee,
- &
- total_deposit_without_refunds,
+ &total_deposit_without_refunds,
-1);
}
}
/* Check other details of wire transfer match */
- if (0 != GNUNET_memcmp (h_wire,
- &wcc->h_wire))
+ if (0 != strcmp (account_pay_uri,
+ wcc->payto_uri))
{
report_row_inconsistency ("aggregation",
rowid,
@@ -1029,16 +986,16 @@ get_wire_fee (struct AggregationContext *ac,
* @param rowid identifier of the respective row in the database
* @param date timestamp of the wire transfer (roughly)
* @param wtid wire transfer subject
- * @param wire wire transfer details of the receiver
+ * @param payto_uri bank account details of the receiver
* @param amount amount that was wired
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration
*/
-static int
+static enum GNUNET_GenericReturnValue
check_wire_out_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute date,
const struct TALER_WireTransferIdentifierRawP *wtid,
- const json_t *wire,
+ const char *payto_uri,
const struct TALER_Amount *amount)
{
struct AggregationContext *ac = cls;
@@ -1057,7 +1014,7 @@ check_wire_out_cb (void *cls,
TALER_B2S (wtid),
TALER_amount2s (amount),
GNUNET_STRINGS_absolute_time_to_string (date));
- if (NULL == (method = TALER_JSON_wire_to_method (wire)))
+ if (NULL == (method = TALER_payto_get_method (payto_uri)))
{
report_row_inconsistency ("wire_out",
rowid,
@@ -1071,14 +1028,7 @@ check_wire_out_cb (void *cls,
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (amount->currency,
&wcc.total_deposits));
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (wire,
- &wcc.h_wire))
- {
- GNUNET_break (0);
- GNUNET_free (method);
- return GNUNET_SYSERR;
- }
+ wcc.payto_uri = payto_uri;
qs = TALER_ARL_edb->lookup_wire_transfer (TALER_ARL_edb->cls,
wtid,
&wire_transfer_information_cb,
@@ -1178,8 +1128,8 @@ check_wire_out_cb (void *cls,
TALER_ARL_report (report_wire_out_inconsistencies,
GNUNET_JSON_PACK (
- GNUNET_JSON_pack_object_incref ("destination_account",
- (json_t *) wire),
+ GNUNET_JSON_pack_string ("destination_account",
+ payto_uri),
GNUNET_JSON_pack_uint64 ("rowid",
rowid),
TALER_JSON_pack_amount ("expected",
diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c
index ba9cf6d1..4267cd0c 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -282,7 +282,7 @@ report_emergency_by_amount (
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Reporting emergency on denomination `%s' over loss of %s\n",
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (loss));
TALER_ARL_report (report_emergencies,
GNUNET_JSON_PACK (
@@ -671,7 +671,7 @@ struct CoinContext
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
-init_denomination (const struct GNUNET_HashCode *denom_hash,
+init_denomination (const struct TALER_DenominationHash *denom_hash,
struct DenominationSummary *ds)
{
enum GNUNET_DB_QueryStatus qs;
@@ -711,7 +711,7 @@ init_denomination (const struct GNUNET_HashCode *denom_hash,
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Starting balance for denomination `%s' is %s (%llu)\n",
- GNUNET_h2s (denom_hash),
+ GNUNET_h2s (&denom_hash->hash),
TALER_amount2s (&ds->denom_balance),
(unsigned long long) ds->num_issued);
qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls,
@@ -758,12 +758,12 @@ init_denomination (const struct GNUNET_HashCode *denom_hash,
static struct DenominationSummary *
get_denomination_summary (struct CoinContext *cc,
const struct TALER_DenominationKeyValidityPS *issue,
- const struct GNUNET_HashCode *dh)
+ const struct TALER_DenominationHash *dh)
{
struct DenominationSummary *ds;
ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
- dh);
+ &dh->hash);
if (NULL != ds)
return ds;
ds = GNUNET_new (struct DenominationSummary);
@@ -777,7 +777,7 @@ get_denomination_summary (struct CoinContext *cc,
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
- dh,
+ &dh->hash,
ds,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
return ds;
@@ -800,6 +800,9 @@ sync_denomination (void *cls,
void *value)
{
struct CoinContext *cc = cls;
+ struct TALER_DenominationHash denom_h = {
+ .hash = *denom_hash
+ };
struct DenominationSummary *ds = value;
const struct TALER_DenominationKeyValidityPS *issue = ds->issue;
struct GNUNET_TIME_Absolute now;
@@ -818,7 +821,7 @@ sync_denomination (void *cls,
outstanding coins as revenue; and reduce cc->risk exposure. */
if (ds->in_db)
qs = TALER_ARL_adb->del_denomination_balance (TALER_ARL_adb->cls,
- denom_hash);
+ &denom_h);
else
qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
@@ -850,7 +853,7 @@ sync_denomination (void *cls,
(qs = TALER_ARL_adb->insert_historic_denom_revenue (
TALER_ARL_adb->cls,
&TALER_ARL_master_pub,
- denom_hash,
+ &denom_h,
expire_deposit,
&ds->denom_balance,
&ds->denom_recoup)))
@@ -873,7 +876,7 @@ sync_denomination (void *cls,
TALER_amount2s (&ds->denom_balance),
(unsigned long long) ds->num_issued);
cnt = TALER_ARL_edb->count_known_coins (TALER_ARL_edb->cls,
- denom_hash);
+ &denom_h);
if (0 > cnt)
{
/* Failed to obtain count? Bad database */
@@ -902,7 +905,7 @@ sync_denomination (void *cls,
}
if (ds->in_db)
qs = TALER_ARL_adb->update_denomination_balance (TALER_ARL_adb->cls,
- denom_hash,
+ &denom_h,
&ds->denom_balance,
&ds->denom_loss,
&ds->denom_risk,
@@ -910,7 +913,7 @@ sync_denomination (void *cls,
ds->num_issued);
else
qs = TALER_ARL_adb->insert_denomination_balance (TALER_ARL_adb->cls,
- denom_hash,
+ &denom_h,
&ds->denom_balance,
&ds->denom_loss,
&ds->denom_risk,
@@ -953,10 +956,10 @@ sync_denomination (void *cls,
* @param amount_with_fee amount that was withdrawn
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
-static int
+static enum GNUNET_GenericReturnValue
withdraw_cb (void *cls,
uint64_t rowid,
- const struct GNUNET_HashCode *h_blind_ev,
+ const struct TALER_BlindedCoinHash *h_blind_ev,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig,
@@ -965,7 +968,7 @@ withdraw_cb (void *cls,
{
struct CoinContext *cc = cls;
struct DenominationSummary *ds;
- struct GNUNET_HashCode dh;
+ struct TALER_DenominationHash dh;
const struct TALER_DenominationKeyValidityPS *issue;
struct TALER_Amount value;
enum GNUNET_DB_QueryStatus qs;
@@ -1013,7 +1016,7 @@ withdraw_cb (void *cls,
&issue->value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Issued coin in denomination `%s' of total value %s\n",
- GNUNET_h2s (&dh),
+ GNUNET_h2s (&dh.hash),
TALER_amount2s (&value));
ds->num_issued++;
TALER_ARL_amount_add (&ds->denom_balance,
@@ -1021,7 +1024,7 @@ withdraw_cb (void *cls,
&value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' is %s\n",
- GNUNET_h2s (&dh),
+ GNUNET_h2s (&dh.hash),
TALER_amount2s (&ds->denom_balance));
TALER_ARL_amount_add (&total_escrow_balance,
&total_escrow_balance,
@@ -1204,7 +1207,7 @@ check_known_coin (const char *operation,
&total_bad_sig_loss,
loss_potential);
}
- GNUNET_CRYPTO_rsa_signature_free (ci.denom_sig.rsa_signature);
+ TALER_denom_sig_free (&ci.denom_sig);
return qs;
}
@@ -1288,8 +1291,8 @@ refresh_session_cb (void *cls,
.coin_pub = *coin_pub
};
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
- &rmc.h_denom_pub);
+ TALER_denom_pub_hash (denom_pub,
+ &rmc.h_denom_pub);
TALER_amount_hton (&rmc.amount_with_fee,
amount_with_fee);
if (GNUNET_OK !=
@@ -1316,7 +1319,7 @@ refresh_session_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Melting coin %s in denomination `%s' of value %s\n",
TALER_B2S (coin_pub),
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (amount_with_fee));
{
@@ -1452,7 +1455,7 @@ refresh_session_cb (void *cls,
&reveal_ctx.new_issues[i]->value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Created fresh coin in denomination `%s' of value %s\n",
- GNUNET_h2s (&reveal_ctx.new_issues[i]->denom_hash),
+ GNUNET_h2s (&reveal_ctx.new_issues[i]->denom_hash.hash),
TALER_amount2s (&value));
dsi->num_issued++;
TALER_ARL_amount_add (&dsi->denom_balance,
@@ -1463,7 +1466,7 @@ refresh_session_cb (void *cls,
&value);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' is %s\n",
- GNUNET_h2s (&reveal_ctx.new_issues[i]->denom_hash),
+ GNUNET_h2s (&reveal_ctx.new_issues[i]->denom_hash.hash),
TALER_amount2s (&dsi->denom_balance));
TALER_ARL_amount_add (&total_escrow_balance,
&total_escrow_balance,
@@ -1526,7 +1529,7 @@ refresh_session_cb (void *cls,
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' after melt is %s\n",
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (&dso->denom_balance));
}
@@ -1553,36 +1556,18 @@ refresh_session_cb (void *cls,
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param exchange_timestamp when did the exchange get the deposit
- * @param wallet_timestamp when did the contract signing happen
- * @param merchant_pub public key of the merchant
+ * @param deposit deposit details
* @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param coin_sig signature from the coin
- * @param amount_with_fee amount that was deposited including fee
- * @param h_contract_terms hash of the proposal data known to merchant and customer
- * @param refund_deadline by which the merchant advised that he might want
- * to get a refund
- * @param wire_deadline by which the merchant advised that he would like the
- * wire transfer to be executed
- * @param receiver_wire_account wire details for the merchant, NULL from iterate_matching_deposits()
* @param done flag set if the deposit was already executed (or not)
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
-static int
+static enum GNUNET_GenericReturnValue
deposit_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute exchange_timestamp,
- struct GNUNET_TIME_Absolute wallet_timestamp,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_EXCHANGEDB_Deposit *deposit,
const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_Amount *amount_with_fee,
- const struct GNUNET_HashCode *h_contract_terms,
- struct GNUNET_TIME_Absolute refund_deadline,
- struct GNUNET_TIME_Absolute wire_deadline,
- const json_t *receiver_wire_account,
- int done)
+ bool done)
{
struct CoinContext *cc = cls;
const struct TALER_DenominationKeyValidityPS *issue;
@@ -1590,6 +1575,7 @@ deposit_cb (void *cls,
enum GNUNET_DB_QueryStatus qs;
(void) done;
+ (void) exchange_timestamp;
GNUNET_assert (rowid >= ppc.last_deposit_serial_id); /* should be monotonically increasing */
ppc.last_deposit_serial_id = rowid + 1;
@@ -1605,8 +1591,8 @@ deposit_cb (void *cls,
return GNUNET_SYSERR;
return GNUNET_OK;
}
- if (refund_deadline.abs_value_us >
- wire_deadline.abs_value_us)
+ if (deposit->refund_deadline.abs_value_us >
+ deposit->wire_deadline.abs_value_us)
{
report_row_inconsistency ("deposits",
rowid,
@@ -1622,9 +1608,9 @@ deposit_cb (void *cls,
qs = check_known_coin ("deposit",
issue,
rowid,
- coin_pub,
+ &deposit->coin.coin_pub,
denom_pub,
- amount_with_fee);
+ &deposit->amount_with_fee);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
@@ -1634,50 +1620,32 @@ deposit_cb (void *cls,
/* Verify deposit signature */
{
- struct TALER_DepositRequestPS dr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
- .purpose.size = htonl (sizeof (dr)),
- .h_contract_terms = *h_contract_terms,
- .wallet_timestamp = GNUNET_TIME_absolute_hton (wallet_timestamp),
- .refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
- .deposit_fee = issue->fee_deposit,
- .merchant = *merchant_pub,
- .coin_pub = *coin_pub
- };
+ struct TALER_MerchantWireHash h_wire;
+ struct TALER_DenominationHash h_denom_pub;
+ struct TALER_Amount deposit_fee;
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
- &dr.h_denom_pub);
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (receiver_wire_account,
- &dr.h_wire))
- {
- TALER_ARL_report (report_bad_sig_losses,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("operation",
- "deposit"),
- GNUNET_JSON_pack_uint64 ("row",
- rowid),
- TALER_JSON_pack_amount ("loss",
- amount_with_fee),
- GNUNET_JSON_pack_data_auto ("coin_pub",
- coin_pub)));
- TALER_ARL_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount_with_fee);
- if (TALER_ARL_do_abort ())
- return GNUNET_SYSERR;
- return GNUNET_OK;
- }
- TALER_amount_hton (&dr.amount_with_fee,
- amount_with_fee);
+ TALER_denom_pub_hash (denom_pub,
+ &h_denom_pub);
+ TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
+ &deposit->wire_salt,
+ &h_wire);
+ TALER_amount_ntoh (&deposit_fee,
+ &issue->fee_deposit);
/* NOTE: This is one of the operations we might eventually
want to do in parallel in the background to improve
auditor performance! */
if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
- &dr,
- &coin_sig->eddsa_signature,
- &coin_pub->eddsa_pub))
+ TALER_wallet_deposit_verify (&deposit->amount_with_fee,
+ &deposit_fee,
+ &h_wire,
+ &deposit->h_contract_terms,
+ NULL /* h_extensions! */,
+ &h_denom_pub,
+ deposit->timestamp,
+ &deposit->merchant_pub,
+ deposit->refund_deadline,
+ &deposit->coin.coin_pub,
+ &deposit->csig))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
@@ -1686,12 +1654,12 @@ deposit_cb (void *cls,
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
- amount_with_fee),
+ &deposit->amount_with_fee),
GNUNET_JSON_pack_data_auto ("coin_pub",
- coin_pub)));
+ &deposit->coin.coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
- amount_with_fee);
+ &deposit->amount_with_fee);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
@@ -1699,9 +1667,9 @@ deposit_cb (void *cls,
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deposited coin %s in denomination `%s' of value %s\n",
- TALER_B2S (coin_pub),
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (amount_with_fee));
+ TALER_B2S (&deposit->coin.coin_pub),
+ GNUNET_h2s (&issue->denom_hash.hash),
+ TALER_amount2s (&deposit->amount_with_fee));
/* update old coin's denomination balance */
ds = get_denomination_summary (cc,
@@ -1720,11 +1688,11 @@ deposit_cb (void *cls,
if (TALER_ARL_SR_INVALID_NEGATIVE ==
TALER_ARL_amount_subtract_neg (&tmp,
&ds->denom_balance,
- amount_with_fee))
+ &deposit->amount_with_fee))
{
TALER_ARL_amount_add (&ds->denom_loss,
&ds->denom_loss,
- amount_with_fee);
+ &deposit->amount_with_fee);
ds->report_emergency = GNUNET_YES;
}
else
@@ -1733,7 +1701,7 @@ deposit_cb (void *cls,
}
if (-1 == TALER_amount_cmp (&total_escrow_balance,
- amount_with_fee))
+ &deposit->amount_with_fee))
{
/* This can theoretically happen if for example the exchange
never issued any coins (i.e. escrow balance is zero), but
@@ -1745,19 +1713,19 @@ deposit_cb (void *cls,
"subtracting deposit fee from escrow balance",
rowid,
&total_escrow_balance,
- amount_with_fee,
+ &deposit->amount_with_fee,
0);
}
else
{
TALER_ARL_amount_subtract (&total_escrow_balance,
&total_escrow_balance,
- amount_with_fee);
+ &deposit->amount_with_fee);
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' after deposit is %s\n",
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (&ds->denom_balance));
}
@@ -1794,14 +1762,14 @@ deposit_cb (void *cls,
* @param amount_with_fee amount that was deposited including fee
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
-static int
+static enum GNUNET_GenericReturnValue
refund_cb (void *cls,
uint64_t rowid,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig,
- const struct GNUNET_HashCode *h_contract_terms,
+ const struct TALER_PrivateContractHash *h_contract_terms,
uint64_t rtransaction_id,
const struct TALER_Amount *amount_with_fee)
{
@@ -1891,7 +1859,7 @@ refund_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Refunding coin %s in denomination `%s' value %s\n",
TALER_B2S (coin_pub),
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (amount_with_fee));
/* update coin's denomination balance */
@@ -1920,7 +1888,7 @@ refund_cb (void *cls,
&amount_without_fee);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' after refund is %s\n",
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (&ds->denom_balance));
}
/* update total refund fee balance */
@@ -1955,7 +1923,7 @@ check_recoup (struct CoinContext *cc,
const struct TALER_CoinPublicInfo *coin,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
+ const union TALER_DenominationBlindingKeyP *coin_blind)
{
struct DenominationSummary *ds;
enum GNUNET_DB_QueryStatus qs;
@@ -2109,7 +2077,7 @@ recoup_cb (void *cls,
const struct TALER_CoinPublicInfo *coin,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
+ const union TALER_DenominationBlindingKeyP *coin_blind)
{
struct CoinContext *cc = cls;
@@ -2150,11 +2118,11 @@ recoup_refresh_cb (void *cls,
struct GNUNET_TIME_Absolute timestamp,
const struct TALER_Amount *amount,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
- const struct GNUNET_HashCode *old_denom_pub_hash,
+ const struct TALER_DenominationHash *old_denom_pub_hash,
const struct TALER_CoinPublicInfo *coin,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
+ const union TALER_DenominationBlindingKeyP *coin_blind)
{
struct CoinContext *cc = cls;
const struct TALER_DenominationKeyValidityPS *issue;
@@ -2203,7 +2171,7 @@ recoup_refresh_cb (void *cls,
amount);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' after refresh-recoup is %s\n",
- GNUNET_h2s (&issue->denom_hash),
+ GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (&dso->denom_balance));
}
}
@@ -2238,7 +2206,28 @@ check_denomination (
const struct TALER_DenominationKeyValidityPS *issue = &validity->properties;
enum GNUNET_DB_QueryStatus qs;
struct TALER_AuditorSignatureP auditor_sig;
+ struct TALER_Amount coin_value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_deposit;
+ struct TALER_Amount fee_refresh;
+ struct TALER_Amount fee_refund;
+ struct GNUNET_TIME_Absolute start;
+ struct GNUNET_TIME_Absolute end;
+ (void) cls;
+ (void) denom_pub;
+ TALER_amount_ntoh (&coin_value,
+ &issue->value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &issue->fee_withdraw);
+ TALER_amount_ntoh (&fee_deposit,
+ &issue->fee_deposit);
+ TALER_amount_ntoh (&fee_refresh,
+ &issue->fee_refresh);
+ TALER_amount_ntoh (&fee_refund,
+ &issue->fee_refund);
+ start = GNUNET_TIME_absolute_ntoh (issue->start);
+ end = GNUNET_TIME_absolute_ntoh (issue->expire_legal);
qs = TALER_ARL_edb->select_auditor_denom_sig (TALER_ARL_edb->cls,
&issue->denom_hash,
&TALER_ARL_auditor_pub,
@@ -2246,56 +2235,41 @@ check_denomination (
if (0 >= qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Encountered denomination `%s' that this auditor is not auditing!\n",
- GNUNET_h2s (&issue->denom_hash));
+ "Encountered denomination `%s' (%s) valid from %s (%llu-%llu) that this auditor is not auditing!\n",
+ GNUNET_h2s (&issue->denom_hash.hash),
+ TALER_amount2s (&coin_value),
+ GNUNET_STRINGS_absolute_time_to_string (start),
+ (unsigned long long) start.abs_value_us,
+ (unsigned long long) end.abs_value_us);
return; /* skip! */
}
- {
- struct TALER_Amount coin_value;
- struct TALER_Amount fee_withdraw;
- struct TALER_Amount fee_deposit;
- struct TALER_Amount fee_refresh;
- struct TALER_Amount fee_refund;
-
- TALER_amount_ntoh (&coin_value,
- &issue->value);
- TALER_amount_ntoh (&fee_withdraw,
- &issue->fee_withdraw);
- TALER_amount_ntoh (&fee_deposit,
- &issue->fee_deposit);
- TALER_amount_ntoh (&fee_refresh,
- &issue->fee_refresh);
- TALER_amount_ntoh (&fee_refund,
- &issue->fee_refund);
- if (GNUNET_OK !=
- TALER_auditor_denom_validity_verify (
- TALER_ARL_auditor_url,
- &issue->denom_hash,
- &TALER_ARL_master_pub,
- GNUNET_TIME_absolute_ntoh (issue->start),
- GNUNET_TIME_absolute_ntoh (issue->expire_withdraw),
- GNUNET_TIME_absolute_ntoh (issue->expire_deposit),
- GNUNET_TIME_absolute_ntoh (issue->expire_legal),
- &coin_value,
- &fee_withdraw,
- &fee_deposit,
- &fee_refresh,
- &fee_refund,
- &TALER_ARL_auditor_pub,
- &auditor_sig))
- {
- TALER_ARL_report (report_denominations_without_sigs,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("denomination",
- &issue->denom_hash),
- TALER_JSON_pack_amount ("value",
- &coin_value),
- TALER_JSON_pack_time_abs_nbo_human ("start_time",
- issue->start),
- TALER_JSON_pack_time_abs_nbo_human ("end_time",
- issue->
- expire_legal)));
- }
+ if (GNUNET_OK !=
+ TALER_auditor_denom_validity_verify (
+ TALER_ARL_auditor_url,
+ &issue->denom_hash,
+ &TALER_ARL_master_pub,
+ start,
+ GNUNET_TIME_absolute_ntoh (issue->expire_withdraw),
+ GNUNET_TIME_absolute_ntoh (issue->expire_deposit),
+ end,
+ &coin_value,
+ &fee_withdraw,
+ &fee_deposit,
+ &fee_refresh,
+ &fee_refund,
+ &TALER_ARL_auditor_pub,
+ &auditor_sig))
+ {
+ TALER_ARL_report (report_denominations_without_sigs,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("denomination",
+ &issue->denom_hash),
+ TALER_JSON_pack_amount ("value",
+ &coin_value),
+ TALER_JSON_pack_time_abs_human ("start_time",
+ start),
+ TALER_JSON_pack_time_abs_human ("end_time",
+ end)));
}
}
diff --git a/src/auditor/taler-helper-auditor-deposits.c b/src/auditor/taler-helper-auditor-deposits.c
index 58f8226f..f7e1cb0d 100644
--- a/src/auditor/taler-helper-auditor-deposits.c
+++ b/src/auditor/taler-helper-auditor-deposits.c
@@ -112,26 +112,22 @@ test_dc (void *cls,
dcc->last_seen_coin_serial = serial_id;
{
enum GNUNET_DB_QueryStatus qs;
- struct TALER_EXCHANGEDB_Deposit dep = {
- .coin.coin_pub = dc->coin_pub,
- .h_contract_terms = dc->h_contract_terms,
- .merchant_pub = dc->merchant,
- .h_wire = dc->h_wire,
- .refund_deadline = dc->refund_deadline
- };
struct GNUNET_TIME_Absolute exchange_timestamp;
struct TALER_Amount deposit_fee;
- qs = TALER_ARL_edb->have_deposit (TALER_ARL_edb->cls,
- &dep,
- GNUNET_NO /* do not check refund deadline */,
- &deposit_fee,
- &exchange_timestamp);
+ qs = TALER_ARL_edb->have_deposit2 (TALER_ARL_edb->cls,
+ &dc->h_contract_terms,
+ &dc->h_wire,
+ &dc->coin_pub,
+ &dc->merchant,
+ dc->refund_deadline,
+ &deposit_fee,
+ &exchange_timestamp);
if (qs > 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Found deposit %s in exchange database\n",
- GNUNET_h2s (&dc->h_contract_terms));
+ GNUNET_h2s (&dc->h_contract_terms.hash));
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK; /* found, all good */
diff --git a/src/auditor/taler-helper-auditor-reserves.c b/src/auditor/taler-helper-auditor-reserves.c
index aa9c241b..52a7b074 100644
--- a/src/auditor/taler-helper-auditor-reserves.c
+++ b/src/auditor/taler-helper-auditor-reserves.c
@@ -487,10 +487,10 @@ handle_reserve_in (void *cls,
* @param amount_with_fee amount that was withdrawn
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
-static int
+static enum GNUNET_GenericReturnValue
handle_reserve_out (void *cls,
uint64_t rowid,
- const struct GNUNET_HashCode *h_blind_ev,
+ const struct TALER_BlindedCoinHash *h_blind_ev,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig,
@@ -689,7 +689,7 @@ handle_recoup_by_reserve (
const struct TALER_CoinPublicInfo *coin,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
+ const union TALER_DenominationBlindingKeyP *coin_blind)
{
struct ReserveContext *rc = cls;
struct GNUNET_HashCode key;
@@ -739,7 +739,7 @@ handle_recoup_by_reserve (
/* check that the coin was eligible for recoup!*/
rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
- &coin->denom_pub_hash);
+ &coin->denom_pub_hash.hash);
if (NULL == rev)
{
qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls,
@@ -777,7 +777,8 @@ handle_recoup_by_reserve (
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (rc->revoked,
- &coin->denom_pub_hash,
+ &coin->denom_pub_hash.
+ hash,
(void *) rev,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@@ -1083,10 +1084,13 @@ verify_reserve_balance (void *cls,
internal audit, as otherwise the balance of the 'reserves' table
is not replicated at the auditor. */
struct TALER_EXCHANGEDB_Reserve reserve;
+ struct TALER_EXCHANGEDB_KycStatus kyc;
reserve.pub = rs->reserve_pub;
qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls,
- &reserve);
+ &reserve,
+ &kyc);
+ // FIXME: figure out what to do with KYC status!
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
/* If the exchange doesn't have this reserve in the summary, it
diff --git a/src/auditor/taler-helper-auditor-wire.c b/src/auditor/taler-helper-auditor-wire.c
index 2cd076ac..f275a453 100644
--- a/src/auditor/taler-helper-auditor-wire.c
+++ b/src/auditor/taler-helper-auditor-wire.c
@@ -775,7 +775,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
* @param rowid deposit table row of the coin's deposit
* @param coin_pub public key of the coin
* @param amount value of the deposit, including fee
- * @param wire where should the funds be wired
+ * @param payto_uri where should the funds be wired
* @param deadline what was the requested wire transfer deadline
* @param tiny did the exchange defer this transfer because it is too small?
* NOTE: only valid in internal audit mode!
@@ -787,10 +787,10 @@ wire_missing_cb (void *cls,
uint64_t rowid,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount,
- const json_t *wire,
+ const char *payto_uri,
struct GNUNET_TIME_Absolute deadline,
- /* bool? */ int tiny,
- /* bool? */ int done)
+ bool tiny,
+ bool done)
{
json_t *rep;
@@ -802,9 +802,9 @@ wire_missing_cb (void *cls,
{
/* In internal mode, we insist that the entry was
actually marked as tiny. */
- if ( (GNUNET_YES == tiny) &&
- (0 > TALER_amount_cmp (amount,
- &tiny_amount)) )
+ if (tiny &&
+ (0 > TALER_amount_cmp (amount,
+ &tiny_amount)) )
return; /* acceptable, amount was tiny */
}
else
@@ -824,8 +824,8 @@ wire_missing_cb (void *cls,
deadline),
GNUNET_JSON_pack_data_auto ("coin_pub",
coin_pub),
- GNUNET_JSON_pack_object_incref ("account",
- (json_t *) wire));
+ GNUNET_JSON_pack_string ("account",
+ payto_uri));
if (internal_checks)
{
/* the 'done' bit is only useful in 'internal' mode */
@@ -942,16 +942,16 @@ check_time_difference (const char *table,
* @param rowid unique serial ID for the refresh session in our DB
* @param date timestamp of the transfer (roughly)
* @param wtid wire transfer subject
- * @param wire wire transfer details of the receiver
+ * @param payto_uri wire transfer details of the receiver
* @param amount amount that was wired
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
-static int
+static enum GNUNET_GenericReturnValue
wire_out_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute date,
const struct TALER_WireTransferIdentifierRawP *wtid,
- const json_t *wire,
+ const char *payto_uri,
const struct TALER_Amount *amount)
{
struct WireAccount *wa = cls;
@@ -997,62 +997,55 @@ wire_out_cb (void *cls,
return GNUNET_SYSERR;
return GNUNET_OK;
}
+ if (0 != strcasecmp (payto_uri,
+ roi->details.credit_account_uri))
{
- char *payto_uri;
-
- payto_uri = TALER_JSON_wire_to_payto (wire);
- if (0 != strcasecmp (payto_uri,
- roi->details.credit_account_uri))
- {
- /* Destination bank account is wrong in actual wire transfer, so
- we should count the wire transfer as entirely spurious, and
- additionally consider the justified wire transfer as missing. */
- TALER_ARL_report (report_wire_out_inconsistencies,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row",
- rowid),
- TALER_JSON_pack_amount ("amount_wired",
- &roi->details.amount),
- TALER_JSON_pack_amount ("amount_justified",
- &zero),
- GNUNET_JSON_pack_data_auto ("wtid", wtid),
- TALER_JSON_pack_time_abs_human ("timestamp",
- date),
- GNUNET_JSON_pack_string ("diagnostic",
- "receiver account mismatch"),
- GNUNET_JSON_pack_string ("target",
- payto_uri),
- GNUNET_JSON_pack_string ("account_section",
- wa->ai->section_name)));
- TALER_ARL_amount_add (&total_bad_amount_out_plus,
- &total_bad_amount_out_plus,
- &roi->details.amount);
- TALER_ARL_report (report_wire_out_inconsistencies,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row",
- rowid),
- TALER_JSON_pack_amount ("amount_wired",
- &zero),
- TALER_JSON_pack_amount ("amount_justified",
- amount),
- GNUNET_JSON_pack_data_auto ("wtid",
- wtid),
- TALER_JSON_pack_time_abs_human ("timestamp",
- date),
- GNUNET_JSON_pack_string ("diagnostic",
- "receiver account mismatch"),
- GNUNET_JSON_pack_string ("target",
- roi->details.
- credit_account_uri),
- GNUNET_JSON_pack_string ("account_section",
- wa->ai->section_name)));
- TALER_ARL_amount_add (&total_bad_amount_out_minus,
- &total_bad_amount_out_minus,
- amount);
- GNUNET_free (payto_uri);
- goto cleanup;
- }
- GNUNET_free (payto_uri);
+ /* Destination bank account is wrong in actual wire transfer, so
+ we should count the wire transfer as entirely spurious, and
+ additionally consider the justified wire transfer as missing. */
+ TALER_ARL_report (report_wire_out_inconsistencies,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("row",
+ rowid),
+ TALER_JSON_pack_amount ("amount_wired",
+ &roi->details.amount),
+ TALER_JSON_pack_amount ("amount_justified",
+ &zero),
+ GNUNET_JSON_pack_data_auto ("wtid", wtid),
+ TALER_JSON_pack_time_abs_human ("timestamp",
+ date),
+ GNUNET_JSON_pack_string ("diagnostic",
+ "receiver account mismatch"),
+ GNUNET_JSON_pack_string ("target",
+ payto_uri),
+ GNUNET_JSON_pack_string ("account_section",
+ wa->ai->section_name)));
+ TALER_ARL_amount_add (&total_bad_amount_out_plus,
+ &total_bad_amount_out_plus,
+ &roi->details.amount);
+ TALER_ARL_report (report_wire_out_inconsistencies,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("row",
+ rowid),
+ TALER_JSON_pack_amount ("amount_wired",
+ &zero),
+ TALER_JSON_pack_amount ("amount_justified",
+ amount),
+ GNUNET_JSON_pack_data_auto ("wtid",
+ wtid),
+ TALER_JSON_pack_time_abs_human ("timestamp",
+ date),
+ GNUNET_JSON_pack_string ("diagnostic",
+ "receiver account mismatch"),
+ GNUNET_JSON_pack_string ("target",
+ roi->details.
+ credit_account_uri),
+ GNUNET_JSON_pack_string ("account_section",
+ wa->ai->section_name)));
+ TALER_ARL_amount_add (&total_bad_amount_out_minus,
+ &total_bad_amount_out_minus,
+ amount);
+ goto cleanup;
}
if (0 != TALER_amount_cmp (&roi->details.amount,
amount))
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index 44b3c7b5..45498d69 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -472,7 +472,7 @@ echo "===========4: deposit wire target wrong================="
# Original target bank account was 43, changing to 44
SERIAL=`echo "SELECT deposit_serial_id FROM deposits WHERE amount_with_fee_val=3 AND amount_with_fee_frac=0 ORDER BY deposit_serial_id LIMIT 1" | psql $DB -Aqt`
OLD_WIRE=`echo "SELECT wire FROM deposits WHERE deposit_serial_id=${SERIAL};" | psql $DB -Aqt`
-echo "UPDATE deposits SET wire='{\"payto_uri\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"test-salt\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB
+echo "UPDATE deposits SET wire='{\"payto_uri\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"9PE256FT5N3YX8H3F1QCHXVNGWAGG3MHDN4J360TSVWTA6WSSMNB8MY4GN5HQAP89TDZH8ANKEG1FB1FJZMN7ZC6NH6QRB0CDDR4TJ8\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB
run_audit
@@ -1500,7 +1500,7 @@ echo "===========26: deposit wire target malformed ================="
# Expects 'payto_uri', not 'url' (also breaks signature, but we cannot even check that).
SERIAL=`echo "SELECT deposit_serial_id FROM deposits WHERE amount_with_fee_val=3 AND amount_with_fee_frac=0 ORDER BY deposit_serial_id LIMIT 1" | psql $DB -Aqt`
OLD_WIRE=`echo "SELECT wire FROM deposits WHERE deposit_serial_id=${SERIAL};" | psql $DB -Aqt`
-echo "UPDATE deposits SET wire='{\"url\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"test-salt\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB
+echo "UPDATE deposits SET wire='{\"url\":\"payto://x-taler-bank/localhost:8082/44\",\"salt\":\"9PE256FT5N3YX8H3F1QCHXVNGWAGG3MHDN4J360TSVWTA6WSSMNB8MY4GN5HQAP89TDZH8ANKEG1FB1FJZMN7ZC6NH6QRB0CDDR4TJ8\"}' WHERE deposit_serial_id=${SERIAL}" | psql -Aqt $DB
run_audit
diff --git a/src/auditor/test-revocation.sh b/src/auditor/test-revocation.sh
index ad62b518..76682572 100755
--- a/src/auditor/test-revocation.sh
+++ b/src/auditor/test-revocation.sh
@@ -120,8 +120,9 @@ function audit_only () {
# Cleanup to run after the auditor
function post_audit () {
+ echo -n "Cleanup ..."
cleanup
- echo "DONE"
+ echo " DONE"
echo -n "TeXing ."
taler-helper-auditor-render.py test-audit-aggregation.json test-audit-coins.json test-audit-deposits.json test-audit-reserves.json test-audit-wire.json < ../../contrib/auditor-report.tex.j2 > test-report.tex || exit_fail "Renderer failed"
@@ -305,28 +306,6 @@ jq -e .wire_format_inconsistencies[0] < test-audit-wire.json > /dev/null && exit
echo PASS
-echo -n "Check for lag detection... "
-
-# Check wire transfer lag reported (no aggregator!)
-# NOTE: This test is EXPECTED to fail for ~1h after
-# re-generating the test database as we do not
-# report lag of less than 1h (see GRACE_PERIOD in
-# taler-helper-auditor-wire.c)
-if [ $DATABASE_AGE -gt 3600 ]
-then
- jq -e .lag_details[0] < test-audit-wire.json > /dev/null || exit_fail "Lag not detected in run without aggregator at age $DELTA"
-
- LAG=`jq -r .total_amount_lag < test-audit-wire.json`
- if test $LAG = "TESTKUDOS:0"
- then
- exit_fail "Expected total lag to be non-zero"
- fi
- echo "PASS"
-else
- echo "SKIP (database too new)"
-fi
-
-
echo -n "Test for wire amounts... "
WIRED=`jq -r .total_wire_in_delta_plus < test-audit-wire.json`
if test $WIRED != "TESTKUDOS:0"
diff --git a/src/auditordb/auditor-0001.sql b/src/auditordb/auditor-0001.sql
index 3a0d7dd9..0faa890d 100644
--- a/src/auditordb/auditor-0001.sql
+++ b/src/auditordb/auditor-0001.sql
@@ -30,7 +30,7 @@ COMMENT ON TABLE auditor_exchanges
CREATE TABLE IF NOT EXISTS auditor_exchange_signkeys
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,ep_start INT8 NOT NULL
,ep_expire INT8 NOT NULL
,ep_end INT8 NOT NULL
@@ -42,7 +42,7 @@ COMMENT ON TABLE auditor_exchange_signkeys
CREATE TABLE IF NOT EXISTS auditor_progress_reserve
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,last_reserve_in_serial_id INT8 NOT NULL DEFAULT 0
,last_reserve_out_serial_id INT8 NOT NULL DEFAULT 0
,last_reserve_recoup_serial_id INT8 NOT NULL DEFAULT 0
@@ -65,7 +65,7 @@ COMMENT ON TABLE auditor_progress_aggregation
CREATE TABLE IF NOT EXISTS auditor_progress_deposit_confirmation
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,last_deposit_confirmation_serial_id INT8 NOT NULL DEFAULT 0
,PRIMARY KEY (master_pub)
);
@@ -75,7 +75,7 @@ COMMENT ON TABLE auditor_progress_deposit_confirmation
CREATE TABLE IF NOT EXISTS auditor_progress_coin
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,last_withdraw_serial_id INT8 NOT NULL DEFAULT 0
,last_deposit_serial_id INT8 NOT NULL DEFAULT 0
,last_melt_serial_id INT8 NOT NULL DEFAULT 0
@@ -90,12 +90,12 @@ COMMENT ON TABLE auditor_progress_coin
CREATE TABLE IF NOT EXISTS wire_auditor_account_progress
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,account_name TEXT NOT NULL
,last_wire_reserve_in_serial_id INT8 NOT NULL DEFAULT 0
,last_wire_wire_out_serial_id INT8 NOT NULL DEFAULT 0
- ,wire_in_off INT8
- ,wire_out_off INT8
+ ,wire_in_off INT8 NOT NULL
+ ,wire_out_off INT8 NOT NULL
,PRIMARY KEY (master_pub,account_name)
);
COMMENT ON TABLE wire_auditor_account_progress
@@ -104,7 +104,7 @@ COMMENT ON TABLE wire_auditor_account_progress
CREATE TABLE IF NOT EXISTS wire_auditor_progress
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,last_timestamp INT8 NOT NULL
,last_reserve_close_uuid INT8 NOT NULL
,PRIMARY KEY (master_pub)
@@ -113,7 +113,7 @@ CREATE TABLE IF NOT EXISTS wire_auditor_progress
CREATE TABLE IF NOT EXISTS auditor_reserves
(reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32)
- ,master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ ,master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,reserve_balance_val INT8 NOT NULL
,reserve_balance_frac INT4 NOT NULL
,withdraw_fee_balance_val INT8 NOT NULL
@@ -131,7 +131,7 @@ CREATE INDEX IF NOT EXISTS auditor_reserves_by_reserve_pub
CREATE TABLE IF NOT EXISTS auditor_reserve_balance
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,reserve_balance_val INT8 NOT NULL
,reserve_balance_frac INT4 NOT NULL
,withdraw_fee_balance_val INT8 NOT NULL
@@ -142,7 +142,7 @@ COMMENT ON TABLE auditor_reserve_balance
CREATE TABLE IF NOT EXISTS auditor_wire_fee_balance
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,wire_fee_balance_val INT8 NOT NULL
,wire_fee_balance_frac INT4 NOT NULL
);
@@ -173,7 +173,7 @@ COMMENT ON COLUMN auditor_denomination_pending.recoup_loss_val
CREATE TABLE IF NOT EXISTS auditor_balance_summary
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,denom_balance_val INT8 NOT NULL
,denom_balance_frac INT4 NOT NULL
,deposit_fee_balance_val INT8 NOT NULL
@@ -194,7 +194,7 @@ COMMENT ON TABLE auditor_balance_summary
CREATE TABLE IF NOT EXISTS auditor_historic_denomination_revenue
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
,revenue_timestamp INT8 NOT NULL
,revenue_balance_val INT8 NOT NULL
@@ -209,7 +209,7 @@ COMMENT ON COLUMN auditor_historic_denomination_revenue.revenue_balance_val
CREATE TABLE IF NOT EXISTS auditor_historic_reserve_summary
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,start_date INT8 NOT NULL
,end_date INT8 NOT NULL
,reserve_profits_val INT8 NOT NULL
@@ -225,19 +225,21 @@ CREATE INDEX IF NOT EXISTS auditor_historic_reserve_summary_by_master_pub_start_
CREATE TABLE IF NOT EXISTS deposit_confirmations
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
- ,serial_id BIGSERIAL UNIQUE
- ,h_contract_terms BYTEA CHECK (LENGTH(h_contract_terms)=64)
- ,h_wire BYTEA CHECK (LENGTH(h_wire)=64)
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ ,serial_id BIGSERIAL NOT NULL UNIQUE
+ ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
+ ,h_extensions BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
+ ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)
,exchange_timestamp INT8 NOT NULL
,refund_deadline INT8 NOT NULL
+ ,wire_deadline INT8 NOT NULL
,amount_without_fee_val INT8 NOT NULL
,amount_without_fee_frac INT4 NOT NULL
- ,coin_pub BYTEA CHECK (LENGTH(coin_pub)=32)
- ,merchant_pub BYTEA CHECK (LENGTH(merchant_pub)=32)
- ,exchange_sig BYTEA CHECK (LENGTH(exchange_sig)=64)
- ,exchange_pub BYTEA CHECK (LENGTH(exchange_pub)=32)
- ,master_sig BYTEA CHECK (LENGTH(master_sig)=64)
+ ,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)
+ ,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)
+ ,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64)
+ ,exchange_pub BYTEA NOT NULL CHECK (LENGTH(exchange_pub)=32)
+ ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,PRIMARY KEY (h_contract_terms,h_wire,coin_pub,merchant_pub,exchange_sig,exchange_pub,master_sig)
);
COMMENT ON TABLE deposit_confirmations
@@ -245,7 +247,7 @@ COMMENT ON TABLE deposit_confirmations
CREATE TABLE IF NOT EXISTS auditor_predicted_result
- (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
+ (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,balance_val INT8 NOT NULL
,balance_frac INT4 NOT NULL
);
diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c
index be221a8b..5817c880 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -188,8 +188,10 @@ setup_connection (struct PostgresClosure *pg)
"INSERT INTO deposit_confirmations "
"(master_pub"
",h_contract_terms"
+ ",h_extensions"
",h_wire"
",exchange_timestamp"
+ ",wire_deadline"
",refund_deadline"
",amount_without_fee_val"
",amount_without_fee_frac"
@@ -198,15 +200,17 @@ setup_connection (struct PostgresClosure *pg)
",exchange_sig"
",exchange_pub"
",master_sig" /* master_sig could be normalized... */
- ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);",
- 12),
+ ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14);",
+ 14),
/* Used in #postgres_get_deposit_confirmations() */
GNUNET_PQ_make_prepare ("auditor_deposit_confirmation_select",
"SELECT"
" serial_id"
",h_contract_terms"
+ ",h_extensions"
",h_wire"
",exchange_timestamp"
+ ",wire_deadline"
",refund_deadline"
",amount_without_fee_val"
",amount_without_fee_frac"
@@ -1029,8 +1033,10 @@ postgres_insert_deposit_confirmation (
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&dc->master_public_key),
GNUNET_PQ_query_param_auto_from_type (&dc->h_contract_terms),
+ GNUNET_PQ_query_param_auto_from_type (&dc->h_extensions),
GNUNET_PQ_query_param_auto_from_type (&dc->h_wire),
TALER_PQ_query_param_absolute_time (&dc->exchange_timestamp),
+ TALER_PQ_query_param_absolute_time (&dc->wire_deadline),
TALER_PQ_query_param_absolute_time (&dc->refund_deadline),
TALER_PQ_query_param_amount (&dc->amount_without_fee),
GNUNET_PQ_query_param_auto_from_type (&dc->coin_pub),
@@ -1108,12 +1114,16 @@ deposit_confirmation_cb (void *cls,
&serial_id),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&dc.h_contract_terms),
+ GNUNET_PQ_result_spec_auto_from_type ("h_extensions",
+ &dc.h_extensions),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&dc.h_wire),
GNUNET_PQ_result_spec_absolute_time ("exchange_timestamp",
&dc.exchange_timestamp),
GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
&dc.refund_deadline),
+ GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+ &dc.wire_deadline),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_without_fee",
&dc.amount_without_fee),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
@@ -2153,7 +2163,7 @@ postgres_get_wire_fee_summary (void *cls,
static enum GNUNET_DB_QueryStatus
postgres_insert_denomination_balance (
void *cls,
- const struct GNUNET_HashCode *denom_pub_hash,
+ const struct TALER_DenominationHash *denom_pub_hash,
const struct TALER_Amount *denom_balance,
const struct TALER_Amount *denom_loss,
const struct TALER_Amount *denom_risk,
@@ -2193,7 +2203,7 @@ postgres_insert_denomination_balance (
static enum GNUNET_DB_QueryStatus
postgres_update_denomination_balance (
void *cls,
- const struct GNUNET_HashCode *denom_pub_hash,
+ const struct TALER_DenominationHash *denom_pub_hash,
const struct TALER_Amount *denom_balance,
const struct TALER_Amount *denom_loss,
const struct TALER_Amount *denom_risk,
@@ -2231,7 +2241,8 @@ postgres_update_denomination_balance (
*/
static enum GNUNET_DB_QueryStatus
postgres_get_denomination_balance (void *cls,
- const struct GNUNET_HashCode *denom_pub_hash,
+ const struct
+ TALER_DenominationHash *denom_pub_hash,
struct TALER_Amount *denom_balance,
struct TALER_Amount *denom_loss,
struct TALER_Amount *denom_risk,
@@ -2428,7 +2439,7 @@ static enum GNUNET_DB_QueryStatus
postgres_insert_historic_denom_revenue (
void *cls,
const struct TALER_MasterPublicKeyP *master_pub,
- const struct GNUNET_HashCode *denom_pub_hash,
+ const struct TALER_DenominationHash *denom_pub_hash,
struct GNUNET_TIME_Absolute revenue_timestamp,
const struct TALER_Amount *revenue_balance,
const struct TALER_Amount *loss_balance)
@@ -2495,7 +2506,7 @@ historic_denom_revenue_cb (void *cls,
for (unsigned int i = 0; i < num_results; i++)
{
- struct GNUNET_HashCode denom_pub_hash;
+ struct TALER_DenominationHash denom_pub_hash;
struct GNUNET_TIME_Absolute revenue_timestamp;
struct TALER_Amount revenue_balance;
struct TALER_Amount loss;
diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c
index 237d8ec8..38f0c24f 100644
--- a/src/auditordb/test_auditordb.c
+++ b/src/auditordb/test_auditordb.c
@@ -130,23 +130,28 @@ run (void *cls)
struct TALER_MasterPublicKeyP master_pub;
struct TALER_ReservePublicKeyP reserve_pub;
- struct GNUNET_HashCode rnd_hash;
- RND_BLK (&master_pub);
- RND_BLK (&reserve_pub);
- RND_BLK (&rnd_hash);
-
+ struct TALER_DenominationHash rnd_hash;
struct TALER_DenominationPrivateKey denom_priv;
struct TALER_DenominationPublicKey denom_pub;
- struct GNUNET_HashCode denom_pub_hash;
+ struct TALER_DenominationHash denom_pub_hash;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Absolute past;
+ struct GNUNET_TIME_Absolute future;
+ struct GNUNET_TIME_Absolute date;
- denom_priv.rsa_private_key = GNUNET_CRYPTO_rsa_private_key_create (1024);
- denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_private_key_get_public (
- denom_priv.rsa_private_key);
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key, &denom_pub_hash);
- GNUNET_CRYPTO_rsa_private_key_free (denom_priv.rsa_private_key);
- GNUNET_CRYPTO_rsa_public_key_free (denom_pub.rsa_public_key);
+ RND_BLK (&master_pub);
+ RND_BLK (&reserve_pub);
+ RND_BLK (&rnd_hash);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_priv_create (&denom_priv,
+ &denom_pub,
+ TALER_DENOMINATION_RSA,
+ 1024));
+ TALER_denom_pub_hash (&denom_pub,
+ &denom_pub_hash);
+ TALER_denom_priv_free (&denom_priv);
+ TALER_denom_pub_free (&denom_pub);
- struct GNUNET_TIME_Absolute now, past, future, date;
now = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&now);
past = GNUNET_TIME_absolute_subtract (now,
@@ -480,14 +485,12 @@ run (void *cls)
"Test: select_historic_denom_revenue\n");
int
- select_historic_denom_revenue_result (void *cls,
- const struct
- GNUNET_HashCode *denom_pub_hash2,
- struct GNUNET_TIME_Absolute
- revenue_timestamp2,
- const struct
- TALER_Amount *revenue_balance2,
- const struct TALER_Amount *loss2)
+ select_historic_denom_revenue_result (
+ void *cls,
+ const struct TALER_DenominationHash *denom_pub_hash2,
+ struct GNUNET_TIME_Absolute revenue_timestamp2,
+ const struct TALER_Amount *revenue_balance2,
+ const struct TALER_Amount *loss2)
{
static int n = 0;
diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c
index 91fbe506..eff2ad97 100644
--- a/src/bank-lib/bank_api_credit.c
+++ b/src/bank-lib/bank_api_credit.c
@@ -30,6 +30,13 @@
/**
+ * How much longer than the application-specified timeout
+ * do we wait (giving the server a chance to respond)?
+ */
+#define GRACE_PERIOD_MS 1000
+
+
+/**
* @brief A /history/incoming Handle
*/
struct TALER_BANK_CreditHistoryHandle
@@ -66,7 +73,7 @@ struct TALER_BANK_CreditHistoryHandle
* were set,
* #GNUNET_SYSERR if there was a protocol violation in @a history
*/
-static int
+static enum GNUNET_GenericReturnValue
parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
const json_t *history)
{
@@ -305,7 +312,7 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
GNUNET_break (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_TIMEOUT_MS,
- (long) tms));
+ (long) tms + GRACE_PERIOD_MS));
}
hh->job = GNUNET_CURL_job_add2 (ctx,
eh,
diff --git a/src/bank-lib/bank_api_debit.c b/src/bank-lib/bank_api_debit.c
index 8cca0cc5..339dfef4 100644
--- a/src/bank-lib/bank_api_debit.c
+++ b/src/bank-lib/bank_api_debit.c
@@ -30,6 +30,13 @@
/**
+ * How much longer than the application-specified timeout
+ * do we wait (giving the server a chance to respond)?
+ */
+#define GRACE_PERIOD_MS 1000
+
+
+/**
* @brief A /history/outgoing Handle
*/
struct TALER_BANK_DebitHistoryHandle
@@ -307,7 +314,7 @@ TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
GNUNET_break (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_TIMEOUT_MS,
- (long) tms));
+ (long) tms + GRACE_PERIOD_MS));
}
hh->job = GNUNET_CURL_job_add2 (ctx,
eh,
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index 2a53419b..e0d8e155 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -425,6 +425,11 @@ struct TALER_FAKEBANK_Handle
*/
bool in_shutdown;
+ /**
+ * Should we run MHD immediately again?
+ */
+ bool mhd_again;
+
#if EPOLL_SUPPORT
/**
* Boxed @e mhd_fd.
@@ -474,6 +479,7 @@ lp_trigger (struct LongPoller *lp,
lp);
MHD_resume_connection (lp->conn);
GNUNET_free (lp);
+ h->mhd_again = true;
if (NULL != h->mhd_task)
GNUNET_SCHEDULER_cancel (h->mhd_task);
h->mhd_task =
@@ -508,12 +514,8 @@ lp_expiration_thread (void *cls)
{
GNUNET_assert (lp ==
GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
lp_trigger (lp,
h);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
}
if (NULL != lp)
@@ -546,8 +548,9 @@ lp_expiration_thread (void *cls)
timeout_ms);
if (-1 == ret)
{
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "poll");
+ if (EINTR != errno)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "poll");
}
else if (1 == ret)
{
@@ -674,7 +677,7 @@ check_log (struct TALER_FAKEBANK_Handle *h)
}
-int
+enum GNUNET_GenericReturnValue
TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
const struct TALER_Amount *want_amount,
const char *want_debit,
@@ -722,7 +725,7 @@ TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
}
-int
+enum GNUNET_GenericReturnValue
TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
const struct TALER_Amount *want_amount,
const char *want_debit,
@@ -866,36 +869,6 @@ post_transaction (struct TALER_FAKEBANK_Handle *h,
ca->in_tail,
old);
}
- {
- struct LongPoller *nxt;
-
- for (struct LongPoller *lp = debit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if (LP_DEBIT == lp->type)
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
- for (struct LongPoller *lp = credit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if (LP_CREDIT == lp->type)
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
- }
GNUNET_assert (0 ==
pthread_mutex_unlock (&h->big_lock));
if ( (NULL != old) &&
@@ -915,6 +888,54 @@ post_transaction (struct TALER_FAKEBANK_Handle *h,
/**
+ * Trigger long pollers that might have been waiting
+ * for @a t.
+ *
+ * @param h fakebank handle
+ * @param t transaction to notify on
+ */
+static void
+notify_transaction (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+ struct LongPoller *nxt;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ for (struct LongPoller *lp = debit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if (LP_DEBIT == lp->type)
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ lp_trigger (lp,
+ h);
+ }
+ }
+ for (struct LongPoller *lp = credit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if (LP_CREDIT == lp->type)
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ lp_trigger (lp,
+ h);
+ }
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+}
+
+
+/**
* Tell the fakebank to create another wire transfer *from* an exchange.
*
* @param h fake bank handle
@@ -929,7 +950,7 @@ post_transaction (struct TALER_FAKEBANK_Handle *h,
* @return #GNUNET_YES if the transfer was successful,
* #GNUNET_SYSERR if the request_uid was reused for a different transfer
*/
-static int
+static enum GNUNET_GenericReturnValue
make_transfer (
struct TALER_FAKEBANK_Handle *h,
const char *debit_account,
@@ -948,6 +969,8 @@ make_transfer (
GNUNET_assert (0 == strcasecmp (amount->currency,
h->currency));
+ GNUNET_assert (NULL != debit_account);
+ GNUNET_assert (NULL != credit_account);
GNUNET_break (0 != strncasecmp ("payto://",
debit_account,
strlen ("payto://")));
@@ -1030,6 +1053,8 @@ make_transfer (
TALER_B2S (subject),
exchange_base_url);
*ret_row_id = t->row_id;
+ notify_transaction (h,
+ t);
return GNUNET_OK;
}
@@ -1124,11 +1149,13 @@ make_admin_transfer (
TALER_amount2s (amount),
TALER_B2S (reserve_pub),
(unsigned long long) t->row_id);
+ notify_transaction (h,
+ t);
return GNUNET_OK;
}
-int
+enum GNUNET_GenericReturnValue
TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
{
for (uint64_t i = 0; i<h->ram_limit; i++)
@@ -1155,6 +1182,8 @@ free_account (void *cls,
{
struct Account *account = val;
+ (void) cls;
+ (void) key;
GNUNET_assert (NULL == account->lp_head);
GNUNET_free (account->account_name);
GNUNET_free (account);
@@ -1358,6 +1387,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Currency `%s' does not match our configuration\n",
amount.currency);
+ json_decref (json);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_CONFLICT,
@@ -1383,6 +1413,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Reserve public key not unique\n");
+ json_decref (json);
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_CONFLICT,
@@ -1647,8 +1678,8 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
if ( (NULL != long_poll_ms) &&
(1 != sscanf (long_poll_ms,
@@ -1666,8 +1697,8 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"long_poll_ms"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
if ( (NULL != start) &&
(1 != sscanf (start,
@@ -1685,8 +1716,8 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"start"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
if (NULL == start)
ha->start_idx = (d > 0) ? 0 : h->serial_counter;
@@ -1701,8 +1732,8 @@ parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
}
ha->lp_timeout
= GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
@@ -1852,6 +1883,7 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
connection,
&ha)))
{
+ GNUNET_break_op (0);
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
}
if (&special_ptr == *con_cls)
@@ -2056,13 +2088,15 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
enum GNUNET_GenericReturnValue ret;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/incoming connection %p\n",
- connection);
+ "Handling /history/incoming connection %p (%d)\n",
+ connection,
+ (*con_cls == &special_ptr));
if (GNUNET_OK !=
(ret = parse_history_common_args (h,
connection,
&ha)))
{
+ GNUNET_break_op (0);
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
}
if (&special_ptr == *con_cls)
@@ -2107,6 +2141,8 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
if ( (NULL == t) ||
overflow)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No transactions available, suspending request\n");
GNUNET_free (credit_payto);
if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
(0 < ha.delta))
@@ -2278,8 +2314,9 @@ serve (struct TALER_FAKEBANK_Handle *h,
else if (0 == strcasecmp (method,
MHD_HTTP_METHOD_POST))
{
- if (0 == strcmp (url,
- "/admin/add-incoming"))
+ if ( (0 == strcmp (url,
+ "/admin/add-incoming")) &&
+ (NULL != account) )
return handle_admin_add_incoming (h,
connection,
account,
@@ -2478,7 +2515,12 @@ run_mhd (void *cls)
struct TALER_FAKEBANK_Handle *h = cls;
h->mhd_task = NULL;
- MHD_run (h->mhd_bank);
+ h->mhd_again = true;
+ while (h->mhd_again)
+ {
+ h->mhd_again = false;
+ MHD_run (h->mhd_bank);
+ }
schedule_httpd (h);
}
diff --git a/src/bank-lib/taler-exchange-wire-gateway-client.c b/src/bank-lib/taler-exchange-wire-gateway-client.c
index 47df82a8..0af3d266 100644
--- a/src/bank-lib/taler-exchange-wire-gateway-client.c
+++ b/src/bank-lib/taler-exchange-wire-gateway-client.c
@@ -179,9 +179,7 @@ credit_history_cb (void *cls,
if (MHD_HTTP_OK != http_status)
{
if ( (MHD_HTTP_NO_CONTENT != http_status) ||
- (TALER_EC_NONE != ec) ||
- ( (MHD_HTTP_NO_CONTENT != http_status) &&
- (NULL == details) ) )
+ (TALER_EC_NONE != ec) )
{
if (0 == http_status)
{
@@ -294,9 +292,7 @@ debit_history_cb (void *cls,
if (MHD_HTTP_OK != http_status)
{
if ( (MHD_HTTP_NO_CONTENT != http_status) ||
- (TALER_EC_NONE != ec) ||
- ( (MHD_HTTP_NO_CONTENT != http_status) &&
- (NULL == details) ) )
+ (TALER_EC_NONE != ec) )
{
if (0 == http_status)
{
@@ -477,6 +473,7 @@ execute_wire_transfer (void)
buf_size,
&confirmation_cb,
NULL);
+ GNUNET_free (buf);
if (NULL == eh)
{
fprintf (stderr,
diff --git a/src/bank-lib/test_bank.sh b/src/bank-lib/test_bank.sh
index 694fb82f..46be326b 100755
--- a/src/bank-lib/test_bank.sh
+++ b/src/bank-lib/test_bank.sh
@@ -2,6 +2,12 @@
set -eu
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo $1
+ exit 77
+}
+
# Cleanup to run whenever we exit
function cleanup()
{
diff --git a/src/benchmark/taler-aggregator-benchmark.c b/src/benchmark/taler-aggregator-benchmark.c
index e432d8f5..fbe4c436 100644
--- a/src/benchmark/taler-aggregator-benchmark.c
+++ b/src/benchmark/taler-aggregator-benchmark.c
@@ -59,11 +59,6 @@ static unsigned int refund_rate = 0;
static char *currency;
/**
- * Merchant JSON wire details.
- */
-static json_t *json_wire;
-
-/**
* Configuration.
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
@@ -81,7 +76,7 @@ static struct GNUNET_SCHEDULER_Task *task;
/**
* Hash of the denomination.
*/
-static struct GNUNET_HashCode h_denom_pub;
+static struct TALER_DenominationHash h_denom_pub;
/**
* "signature" to use for the coin(s).
@@ -140,13 +135,11 @@ make_amount (unsigned int val,
unsigned int frac,
struct TALER_Amount *out)
{
- memset (out,
- 0,
- sizeof (struct TALER_Amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (currency,
+ out));
out->value = val;
out->fraction = frac;
- strcpy (out->currency,
- currency);
}
@@ -212,16 +205,7 @@ do_shutdown (void *cls)
GNUNET_SCHEDULER_cancel (task);
task = NULL;
}
- if (NULL !=denom_sig.rsa_signature)
- {
- GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
- denom_sig.rsa_signature = NULL;
- }
- if (NULL != json_wire)
- {
- json_decref (json_wire);
- json_wire = NULL;
- }
+ TALER_denom_sig_free (&denom_sig);
}
@@ -240,7 +224,17 @@ struct Merchant
* the exchange from the detailed wire data provided by the
* merchant.
*/
- struct GNUNET_HashCode h_wire;
+ struct TALER_MerchantWireHash h_wire;
+
+ /**
+ * Salt used when computing @e h_wire.
+ */
+ struct TALER_WireSalt wire_salt;
+
+ /**
+ * Account information for the merchant.
+ */
+ char *payto_uri;
};
@@ -256,7 +250,7 @@ struct Deposit
* Hash over the proposal data between merchant and customer
* (remains unknown to the Exchange).
*/
- struct GNUNET_HashCode h_contract_terms;
+ struct TALER_PrivateContractHash h_contract_terms;
};
@@ -324,9 +318,8 @@ add_deposit (const struct Merchant *m)
RANDOMIZE (&deposit.csig);
deposit.merchant_pub = m->merchant_pub;
deposit.h_contract_terms = d.h_contract_terms;
- deposit.h_wire = m->h_wire;
- deposit.receiver_wire_account
- = json_wire;
+ deposit.wire_salt = m->wire_salt;
+ deposit.receiver_wire_account = m->payto_uri;
deposit.timestamp = random_time ();
deposit.refund_deadline = random_time ();
deposit.wire_deadline = random_time ();
@@ -359,7 +352,6 @@ static void
work (void *cls)
{
struct Merchant m;
- char *acc;
uint64_t rnd1;
uint64_t rnd2;
@@ -369,33 +361,22 @@ work (void *cls)
UINT64_MAX);
rnd2 = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
UINT64_MAX);
- GNUNET_asprintf (&acc,
+ GNUNET_asprintf (&m.payto_uri,
"payto://x-taler-bank/localhost:8082/account-%llX-%llX",
(unsigned long long) rnd1,
(unsigned long long) rnd2);
- json_wire = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("payto_uri",
- acc),
- GNUNET_JSON_pack_string ("salt",
- "thesalty"));
- GNUNET_free (acc);
RANDOMIZE (&m.merchant_pub);
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (json_wire,
- &m.h_wire))
- {
- GNUNET_break (0);
- global_ret = EXIT_FAILURE;
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
+ RANDOMIZE (&m.wire_salt);
+ TALER_merchant_wire_signature_hash (m.payto_uri,
+ &m.wire_salt,
+ &m.h_wire);
if (GNUNET_OK !=
plugin->start (plugin->cls,
"aggregator-benchmark-fill"))
{
GNUNET_break (0);
global_ret = EXIT_FAILURE;
+ GNUNET_free (m.payto_uri);
GNUNET_SCHEDULER_shutdown ();
return;
}
@@ -405,6 +386,7 @@ work (void *cls)
{
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free (m.payto_uri);
return;
}
}
@@ -414,6 +396,7 @@ work (void *cls)
if (0 == --howmany_merchants)
{
GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free (m.payto_uri);
return;
}
}
@@ -422,8 +405,7 @@ work (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to commit, will try again\n");
}
- json_decref (json_wire);
- json_wire = NULL;
+ GNUNET_free (m.payto_uri);
task = GNUNET_SCHEDULER_add_now (&work,
NULL);
}
@@ -446,6 +428,8 @@ run (void *cls,
struct TALER_EXCHANGEDB_DenominationKeyInformationP issue;
(void) cls;
+ (void) args;
+ (void) cfgfile;
/* make sure everything 'ends' before the current time,
so that the aggregator will process everything without
need for time-travel */
@@ -495,17 +479,22 @@ run (void *cls,
GNUNET_TIME_absolute_add (end,
GNUNET_TIME_UNIT_YEARS));
{
- struct GNUNET_CRYPTO_RsaPrivateKey *pk;
- struct GNUNET_CRYPTO_RsaPublicKey *pub;
- struct GNUNET_HashCode hc;
+ struct TALER_DenominationPrivateKey pk;
struct TALER_DenominationPublicKey denom_pub;
-
- RANDOMIZE (&hc);
- pk = GNUNET_CRYPTO_rsa_private_key_create (1024);
- pub = GNUNET_CRYPTO_rsa_private_key_get_public (pk);
- denom_pub.rsa_public_key = pub;
- GNUNET_CRYPTO_rsa_public_key_hash (pub,
- &h_denom_pub);
+ struct TALER_CoinPubHash c_hash;
+ struct TALER_PlanchetDetail pd;
+ struct TALER_BlindedDenominationSignature bds;
+ union TALER_DenominationBlindingKeyP bks;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ RANDOMIZE (&coin_pub);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_priv_create (&pk,
+ &denom_pub,
+ TALER_DENOMINATION_RSA,
+ 1024));
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
make_amountN (2, 0, &issue.properties.value);
make_amountN (0, 5, &issue.properties.fee_withdraw);
make_amountN (0, 5, &issue.properties.fee_deposit);
@@ -523,11 +512,29 @@ run (void *cls,
return;
}
- denom_sig.rsa_signature
- = GNUNET_CRYPTO_rsa_sign_fdh (pk,
- &hc);
- GNUNET_CRYPTO_rsa_public_key_free (pub);
- GNUNET_CRYPTO_rsa_private_key_free (pk);
+ TALER_blinding_secret_create (&bks);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_blind (&denom_pub,
+ &bks,
+ NULL, /* FIXME-oec */
+ &coin_pub,
+ &c_hash,
+ &pd.coin_ev,
+ &pd.coin_ev_size));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sign_blinded (&bds,
+ &pk,
+ pd.coin_ev,
+ pd.coin_ev_size));
+ GNUNET_free (pd.coin_ev);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_sig_unblind (&denom_sig,
+ &bds,
+ &bks,
+ &denom_pub));
+ TALER_blinded_denom_sig_free (&bds);
+ TALER_denom_pub_free (&denom_pub);
+ TALER_denom_priv_free (&pk);
}
{
diff --git a/src/benchmark/taler-bank-benchmark.c b/src/benchmark/taler-bank-benchmark.c
index b7bcc105..4d7dbe35 100644
--- a/src/benchmark/taler-bank-benchmark.c
+++ b/src/benchmark/taler-bank-benchmark.c
@@ -411,6 +411,7 @@ stop_fakebank (void *cls)
static void
never_task (void *cls)
{
+ (void) cls;
GNUNET_assert (0);
}
diff --git a/src/curl/curl.c b/src/curl/curl.c
index 1410294e..424c41fd 100644
--- a/src/curl/curl.c
+++ b/src/curl/curl.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2019-2020 Taler Systems SA
+ Copyright (C) 2019-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
@@ -24,6 +24,7 @@
#include "platform.h"
#include "taler_curl_lib.h"
+
#if TALER_CURL_COMPRESS_BODIES
#include <zlib.h>
#endif
@@ -37,7 +38,7 @@
* @param body JSON body to add to @e ctx
* @return #GNUNET_OK on success #GNUNET_SYSERR on failure
*/
-int
+enum GNUNET_GenericReturnValue
TALER_curl_easy_post (struct TALER_CURL_PostContext *ctx,
CURL *eh,
const json_t *body)
@@ -76,18 +77,17 @@ TALER_curl_easy_post (struct TALER_CURL_PostContext *ctx,
slen = (size_t) cbuf_size;
ctx->json_enc = (char *) cbuf;
}
- GNUNET_assert
- (NULL != (ctx->headers = curl_slist_append
- (ctx->headers,
- "Content-Encoding: deflate")));
+ GNUNET_assert (NULL != (ctx->headers = curl_slist_append (
+ ctx->headers,
+ "Content-Encoding: deflate")));
#else
ctx->json_enc = str;
#endif
GNUNET_assert
- (NULL != (ctx->headers = curl_slist_append
- (ctx->headers,
- "Content-Type: application/json")));
+ (NULL != (ctx->headers = curl_slist_append (
+ ctx->headers,
+ "Content-Type: application/json")));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
diff --git a/src/exchange-tools/taler-auditor-offline.c b/src/exchange-tools/taler-auditor-offline.c
index 45bf271b..5ff713a4 100644
--- a/src/exchange-tools/taler-auditor-offline.c
+++ b/src/exchange-tools/taler-auditor-offline.c
@@ -430,7 +430,7 @@ upload_denomination_add (const char *exchange_url,
const json_t *value)
{
struct TALER_AuditorSignatureP auditor_sig;
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
struct DenominationAddRequest *dar;
const char *err_name;
unsigned int err_line;
@@ -547,6 +547,7 @@ do_upload (char *const *args)
{
char *exchange_url;
+ (void) args;
if (GNUNET_YES == GNUNET_is_zero (&auditor_pub))
{
/* private key not available, try configuration for public key */
@@ -657,6 +658,8 @@ keys_cb (
{
char *const *args = cls;
+ (void) keys;
+ (void) compat;
switch (hr->http_status)
{
case MHD_HTTP_OK:
@@ -760,8 +763,8 @@ show_denomkeys (const json_t *denomkeys)
struct TALER_Amount fee_refund;
struct TALER_MasterSignatureP master_sig;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_rsa_public_key ("denom_pub",
- &denom_pub.rsa_public_key),
+ TALER_JSON_spec_denom_pub ("denom_pub",
+ &denom_pub),
TALER_JSON_spec_amount ("value",
currency,
&coin_value),
@@ -790,7 +793,7 @@ show_denomkeys (const json_t *denomkeys)
GNUNET_JSON_spec_end ()
};
struct GNUNET_TIME_Relative duration;
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
@@ -810,8 +813,8 @@ show_denomkeys (const json_t *denomkeys)
}
duration = GNUNET_TIME_absolute_get_difference (stamp_start,
stamp_expire_withdraw);
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
- &h_denom_pub);
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
if (GNUNET_OK !=
TALER_exchange_offline_denom_validity_verify (
&h_denom_pub,
@@ -1060,8 +1063,8 @@ sign_denomkeys (const json_t *denomkeys)
struct TALER_Amount fee_refund;
struct TALER_MasterSignatureP master_sig;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_rsa_public_key ("denom_pub",
- &denom_pub.rsa_public_key),
+ TALER_JSON_spec_denom_pub ("denom_pub",
+ &denom_pub),
TALER_JSON_spec_amount ("value",
currency,
&coin_value),
@@ -1089,7 +1092,7 @@ sign_denomkeys (const json_t *denomkeys)
&master_sig),
GNUNET_JSON_spec_end ()
};
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
@@ -1107,8 +1110,8 @@ sign_denomkeys (const json_t *denomkeys)
test_shutdown ();
return GNUNET_SYSERR;
}
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
- &h_denom_pub);
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
if (GNUNET_OK !=
TALER_exchange_offline_denom_validity_verify (
&h_denom_pub,
@@ -1375,6 +1378,8 @@ run (void *cls,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
+ (void) cls;
+ (void) cfgfile;
kcfg = cfg;
if (GNUNET_OK !=
TALER_config_get_currency (kcfg,
diff --git a/src/exchange-tools/taler-exchange-dbinit.c b/src/exchange-tools/taler-exchange-dbinit.c
index acc37be8..32f0570f 100644
--- a/src/exchange-tools/taler-exchange-dbinit.c
+++ b/src/exchange-tools/taler-exchange-dbinit.c
@@ -86,6 +86,7 @@ run (void *cls,
fprintf (stderr,
"Failed to initialize database.\n");
TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
global_ret = EXIT_NOPERMISSION;
return;
}
@@ -97,6 +98,7 @@ run (void *cls,
fprintf (stderr,
"Failed to prepare database.\n");
TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
global_ret = EXIT_NOPERMISSION;
return;
}
@@ -119,6 +121,7 @@ run (void *cls,
}
}
TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
}
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
index 4558f00b..117e6284 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -761,7 +761,7 @@ struct UploadHandler
* @param do_create #GNUNET_YES if the key may be created
* @return #GNUNET_OK on success
*/
-static int
+static enum GNUNET_GenericReturnValue
load_offline_key (int do_create)
{
static bool done;
@@ -852,7 +852,7 @@ upload_denom_revocation (const char *exchange_url,
const json_t *value)
{
struct TALER_MasterSignatureP master_sig;
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
struct DenomRevocationRequest *drr;
const char *err_name;
unsigned int err_line;
@@ -1759,6 +1759,7 @@ trigger_upload (const char *exchange_url)
static void
do_upload (char *const *args)
{
+ (void) args;
if (NULL != in)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1824,7 +1825,7 @@ do_upload (char *const *args)
static void
do_revoke_denomination_key (char *const *args)
{
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
struct TALER_MasterSignatureP master_sig;
if (NULL != in)
@@ -2264,6 +2265,7 @@ download_cb (void *cls,
{
char *const *args = cls;
+ (void) keys;
mgkh = NULL;
switch (hr->http_status)
{
@@ -2604,8 +2606,8 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("section_name",
&section_name),
- GNUNET_JSON_spec_rsa_public_key ("denom_pub",
- &denom_pub.rsa_public_key),
+ TALER_JSON_spec_denom_pub ("denom_pub",
+ &denom_pub),
TALER_JSON_spec_amount ("value",
currency,
&coin_value),
@@ -2634,7 +2636,8 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
GNUNET_JSON_spec_end ()
};
struct GNUNET_TIME_Relative duration;
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
+ enum GNUNET_GenericReturnValue ok;
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
@@ -2657,19 +2660,34 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
}
duration = GNUNET_TIME_absolute_get_difference (stamp_start,
stamp_expire_withdraw);
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
- &h_denom_pub);
- if (GNUNET_OK !=
- TALER_exchange_secmod_rsa_verify (&h_denom_pub,
- section_name,
- stamp_start,
- duration,
- secm_pub,
- &secm_sig))
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
+ switch (denom_pub.cipher)
+ {
+ case TALER_DENOMINATION_RSA:
+ {
+ struct TALER_RsaPubHashP h_rsa;
+
+ TALER_rsa_pub_hash (denom_pub.details.rsa_public_key,
+ &h_rsa);
+ ok = TALER_exchange_secmod_rsa_verify (&h_rsa,
+ section_name,
+ stamp_start,
+ duration,
+ secm_pub,
+ &secm_sig);
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ ok = GNUNET_SYSERR;
+ break;
+ }
+ if (GNUNET_OK != ok)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid security module signature for denomination key %s (aborting)\n",
- GNUNET_h2s (&h_denom_pub));
+ GNUNET_h2s (&h_denom_pub.hash));
global_ret = EXIT_FAILURE;
test_shutdown ();
return GNUNET_SYSERR;
@@ -2997,7 +3015,7 @@ sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
* @param[in,out] result array where to output the signatures
* @return #GNUNET_OK on success
*/
-static int
+static enum GNUNET_GenericReturnValue
sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
const json_t *denomkeys,
json_t *result)
@@ -3023,8 +3041,8 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("section_name",
&section_name),
- GNUNET_JSON_spec_rsa_public_key ("denom_pub",
- &denom_pub.rsa_public_key),
+ TALER_JSON_spec_denom_pub ("denom_pub",
+ &denom_pub),
TALER_JSON_spec_amount ("value",
currency,
&coin_value),
@@ -3053,7 +3071,7 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
GNUNET_JSON_spec_end ()
};
struct GNUNET_TIME_Relative duration;
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
@@ -3076,19 +3094,36 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
}
duration = GNUNET_TIME_absolute_get_difference (stamp_start,
stamp_expire_withdraw);
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
- &h_denom_pub);
- if (GNUNET_OK !=
- TALER_exchange_secmod_rsa_verify (&h_denom_pub,
- section_name,
- stamp_start,
- duration,
- secm_pub,
- &secm_sig))
+ // FIXME-Oec: setup age mask here?
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
+ switch (denom_pub.cipher)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid security module signature for denomination key %s (aborting)\n",
- GNUNET_h2s (&h_denom_pub));
+ case TALER_DENOMINATION_RSA:
+ {
+ struct TALER_RsaPubHashP h_rsa;
+
+ TALER_rsa_pub_hash (denom_pub.details.rsa_public_key,
+ &h_rsa);
+ if (GNUNET_OK !=
+ TALER_exchange_secmod_rsa_verify (&h_rsa,
+ section_name,
+ stamp_start,
+ duration,
+ secm_pub,
+ &secm_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid security module signature for denomination key %s (aborting)\n",
+ GNUNET_h2s (&h_denom_pub.hash));
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ }
+ break;
+ default:
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
@@ -3410,6 +3445,8 @@ run (void *cls,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
+ (void) cls;
+ (void) cfgfile;
kcfg = cfg;
if (GNUNET_OK !=
TALER_config_get_currency (kcfg,
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 1f53b012..7779c38b 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -83,6 +83,9 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
taler-exchange-httpd_keys.c taler-exchange-httpd_keys.h \
+ taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.h \
+ taler-exchange-httpd_kyc-proof.c taler-exchange-httpd_kyc-proof.h \
+ taler-exchange-httpd_kyc-wallet.c taler-exchange-httpd_kyc-wallet.h \
taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
taler-exchange-httpd_management.h \
taler-exchange-httpd_management_auditors.c \
@@ -120,6 +123,7 @@ taler_exchange_httpd_LDADD = \
-lgnunetutil \
-lgnunetjson \
-ljansson \
+ -lcurl \
-lz \
$(XLIB)
diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf
index c4115042..4d353ace 100644
--- a/src/exchange/exchange.conf
+++ b/src/exchange/exchange.conf
@@ -77,9 +77,37 @@ TERMS_DIR = $DATADIR/exchange/tos/
# Etag / filename for the terms of service.
TERMS_ETAG = 0
-
# Directory with our privacy policy.
PRIVACY_DIR = $DATADIR/exchange/pp/
# Etag / filename for the privacy policy.
PRIVACY_ETAG = 0
+
+# Set to NONE to disable KYC checks.
+# Set to "OAUTH2" to use OAuth 2.0 for KYC authorization.
+KYC_MODE = NONE
+
+# Balance threshold above which wallets are told
+# to undergo a KYC check at the exchange. Optional,
+# if not given there is no limit.
+# KYC_WALLET_BALANCE_LIMIT = CURRENCY:150
+#
+# KYC_WITHDRAW_PERIOD = 1 month
+
+[exchange-kyc-oauth2]
+
+# URL of the OAuth endpoint for KYC checks
+# KYC_OAUTH2_URL =
+
+# URL of the "information" endpoint for KYC checks
+# KYC_INFO_URL =
+
+# KYC Oauth client ID.
+# KYC_OAUTH2_CLIENT_ID =
+
+# KYC Client secret used to obtain access tokens.
+# KYC_OAUTH2_CLIENT_SECRET =
+
+# Where to redirect clients after successful
+# authorization?
+# KYC_OAUTH2_POST_URL = https://bank.com/
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
index caa4528d..73bbcc59 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -58,11 +58,6 @@ struct AggregationUnit
struct TALER_Amount wire_fee;
/**
- * Hash of @e wire.
- */
- struct GNUNET_HashCode h_wire;
-
- /**
* Wire transfer identifier we use.
*/
struct TALER_WireTransferIdentifierRawP wtid;
@@ -81,7 +76,12 @@ struct AggregationUnit
/**
* Wire details of the merchant.
*/
- json_t *wire;
+ char *payto_uri;
+
+ /**
+ * Selected wire target for the aggregation.
+ */
+ uint64_t wire_target;
/**
* Exchange wire account to be used for the preparation and
@@ -144,12 +144,19 @@ static struct TALER_Amount currency_round_unit;
/**
* What is the base URL of this exchange? Used in the
- * wire transfer subjects to that merchants and governments
+ * wire transfer subjects so that merchants and governments
* can ask for the list of aggregated deposits.
*/
static char *exchange_base_url;
/**
+ * Set to #GNUNET_YES if this exchange does not support KYC checks
+ * and thus deposits are to be aggregated regardless of the
+ * KYC status of the target account.
+ */
+static int kyc_off;
+
+/**
* The exchange's configuration.
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
@@ -216,8 +223,7 @@ static void
cleanup_au (struct AggregationUnit *au)
{
GNUNET_assert (NULL != au);
- if (NULL != au->wire)
- json_decref (au->wire);
+ GNUNET_free (au->payto_uri);
memset (au,
0,
sizeof (*au));
@@ -320,7 +326,7 @@ parse_wirewatch_config (void)
* @param amount_with_fee what was the refunded amount with the fee
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
-static int
+static enum GNUNET_GenericReturnValue
refund_by_coin_cb (void *cls,
const struct TALER_Amount *amount_with_fee)
{
@@ -353,7 +359,8 @@ refund_by_coin_cb (void *cls,
* @param amount_with_fee amount that was deposited including fee
* @param deposit_fee amount the exchange gets to keep as transaction fees
* @param h_contract_terms hash of the proposal data known to merchant and customer
- * @param wire target account for the wire transfer
+ * @param wire_target target account for the wire transfer
+ * @param payto_uri URI of the target account
* @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to continue to iterate
*/
static enum GNUNET_DB_QueryStatus
@@ -363,8 +370,9 @@ deposit_cb (void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
- const struct GNUNET_HashCode *h_contract_terms,
- const json_t *wire)
+ const struct TALER_PrivateContractHash *h_contract_terms,
+ uint64_t wire_target,
+ const char *payto_uri)
{
struct AggregationUnit *au = cls;
enum GNUNET_DB_QueryStatus qs;
@@ -416,21 +424,9 @@ deposit_cb (void *cls,
}
}
- GNUNET_assert (NULL == au->wire);
- if (NULL == (au->wire = json_incref ((json_t *) wire)))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (wire,
- &au->h_wire))
- {
- GNUNET_break (0);
- json_decref (au->wire);
- au->wire = NULL;
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
+ GNUNET_assert (NULL == au->payto_uri);
+ au->payto_uri = GNUNET_strdup (payto_uri);
+ au->wire_target = wire_target;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&au->wtid,
sizeof (au->wtid));
@@ -439,20 +435,13 @@ deposit_cb (void *cls,
TALER_B2S (&au->wtid),
TALER_amount2s (amount_with_fee),
(unsigned long long) row_id);
+ au->wa = TALER_EXCHANGEDB_find_account_by_payto_uri (payto_uri);
+ if (NULL == au->wa)
{
- char *url;
-
- url = TALER_JSON_wire_to_payto (au->wire);
- au->wa = TALER_EXCHANGEDB_find_account_by_payto_uri (url);
- if (NULL == au->wa)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "No exchange account configured for `%s', please fix your setup to continue!\n",
- url);
- GNUNET_free (url);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- GNUNET_free (url);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No exchange account configured for `%s', please fix your setup to continue!\n",
+ payto_uri);
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
/* make sure we have current fees */
@@ -528,7 +517,7 @@ aggregate_cb (void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
- const struct GNUNET_HashCode *h_contract_terms)
+ const struct TALER_PrivateContractHash *h_contract_terms)
{
struct AggregationUnit *au = cls;
struct TALER_Amount old;
@@ -724,6 +713,7 @@ run_aggregation (void *cls)
db_plugin->cls,
s->shard_start,
s->shard_end,
+ kyc_off ? true : false,
&deposit_cb,
&au_active);
switch (qs)
@@ -786,10 +776,11 @@ run_aggregation (void *cls)
/* Now try to find other deposits to aggregate */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Found ready deposit for %s, aggregating\n",
- TALER_B2S (&au_active.merchant_pub));
+ "Found ready deposit for %s, aggregating by target %llu\n",
+ TALER_B2S (&au_active.merchant_pub),
+ (unsigned long long) au_active.wire_target);
qs = db_plugin->iterate_matching_deposits (db_plugin->cls,
- &au_active.h_wire,
+ au_active.wire_target,
&au_active.merchant_pub,
&aggregate_cb,
&au_active,
@@ -908,19 +899,12 @@ run_aggregation (void *cls)
void *buf;
size_t buf_size;
- {
- char *url;
-
- url = TALER_JSON_wire_to_payto (au_active.wire);
- TALER_BANK_prepare_transfer (url,
- &au_active.final_amount,
- exchange_base_url,
- &au_active.wtid,
- &buf,
- &buf_size);
- GNUNET_free (url);
- }
-
+ TALER_BANK_prepare_transfer (au_active.payto_uri,
+ &au_active.final_amount,
+ exchange_base_url,
+ &au_active.wtid,
+ &buf,
+ &buf_size);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Storing %u bytes of wire prepare data\n",
(unsigned int) buf_size);
@@ -937,7 +921,7 @@ run_aggregation (void *cls)
qs = db_plugin->store_wire_transfer_out (db_plugin->cls,
au_active.execution_time,
&au_active.wtid,
- au_active.wire,
+ au_active.wire_target,
au_active.wa->section_name,
&au_active.final_amount);
cleanup_au (&au_active);
@@ -1130,6 +1114,10 @@ main (int argc,
"test",
"run in test mode and exit when idle",
&test_mode),
+ GNUNET_GETOPT_option_flag ('y',
+ "kyc-off",
+ "perform wire transfers without KYC checks",
+ &kyc_off),
GNUNET_GETOPT_OPTION_END
};
enum GNUNET_GenericReturnValue ret;
diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c
index 19cc06c7..3f30db7b 100644
--- a/src/exchange/taler-exchange-closer.c
+++ b/src/exchange/taler-exchange-closer.c
@@ -37,7 +37,7 @@ static struct TALER_Amount currency_round_unit;
/**
* What is the base URL of this exchange? Used in the
- * wire transfer subjects to that merchants and governments
+ * wire transfer subjects so that merchants and governments
* can ask for the list of aggregated deposits.
*/
static char *exchange_base_url;
@@ -217,7 +217,7 @@ expired_reserve_cb (void *cls,
struct TALER_WireTransferIdentifierRawP wtid;
struct TALER_Amount amount_without_fee;
struct TALER_Amount closing_fee;
- int ret;
+ enum TALER_AmountArithmeticResult ret;
enum GNUNET_DB_QueryStatus qs;
const struct TALER_EXCHANGEDB_AccountInfo *wa;
@@ -273,8 +273,8 @@ expired_reserve_cb (void *cls,
ret = TALER_amount_subtract (&amount_without_fee,
left,
&closing_fee);
- if ( (GNUNET_SYSERR == ret) ||
- (GNUNET_NO == ret) )
+ if ( (TALER_AAR_INVALID_NEGATIVE_RESULT == ret) ||
+ (TALER_AAR_RESULT_ZERO == ret) )
{
/* Closing fee higher than or equal to remaining balance, close
without wire transfer. */
@@ -283,6 +283,7 @@ expired_reserve_cb (void *cls,
TALER_amount_set_zero (left->currency,
&amount_without_fee));
}
+ GNUNET_assert (TALER_AAR_RESULT_POSITIVE == ret);
/* round down to enable transfer */
if (GNUNET_SYSERR ==
TALER_amount_round_down (&amount_without_fee,
@@ -293,10 +294,6 @@ expired_reserve_cb (void *cls,
GNUNET_SCHEDULER_shutdown ();
return GNUNET_DB_STATUS_HARD_ERROR;
}
- if ( (0 == amount_without_fee.value) &&
- (0 == amount_without_fee.fraction) )
- ret = GNUNET_NO;
-
/* NOTE: sizeof (*reserve_pub) == sizeof (wtid) right now, but to
be future-compatible, we use the memset + min construction */
memset (&wtid,
@@ -306,7 +303,7 @@ expired_reserve_cb (void *cls,
reserve_pub,
GNUNET_MIN (sizeof (wtid),
sizeof (*reserve_pub)));
- if (GNUNET_SYSERR != ret)
+ if (TALER_AAR_INVALID_NEGATIVE_RESULT != ret)
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
reserve_pub,
now,
@@ -320,10 +317,10 @@ expired_reserve_cb (void *cls,
"Closing reserve %s over %s (%d, %d)\n",
TALER_B2S (reserve_pub),
TALER_amount2s (left),
- ret,
+ (int) ret,
qs);
/* Check for hard failure */
- if ( (GNUNET_SYSERR == ret) ||
+ if ( (TALER_AAR_INVALID_NEGATIVE_RESULT == ret) ||
(GNUNET_DB_STATUS_HARD_ERROR == qs) )
{
GNUNET_break (0);
@@ -331,10 +328,10 @@ expired_reserve_cb (void *cls,
GNUNET_SCHEDULER_shutdown ();
return GNUNET_DB_STATUS_HARD_ERROR;
}
- if ( (GNUNET_OK != ret) ||
+ if ( (TALER_AAR_RESULT_ZERO == ret) ||
(GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) )
{
- /* Reserve balance was almost zero OR soft error */
+ /* Reserve balance was zero OR soft error */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reserve was virtually empty, moving on\n");
(void) commit_or_warn ();
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index af0a4908..0a8798ae 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -33,6 +33,9 @@
#include "taler-exchange-httpd_deposit.h"
#include "taler-exchange-httpd_deposits_get.h"
#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_kyc-check.h"
+#include "taler-exchange-httpd_kyc-proof.h"
+#include "taler-exchange-httpd_kyc-wallet.h"
#include "taler-exchange-httpd_link.h"
#include "taler-exchange-httpd_management.h"
#include "taler-exchange-httpd_melt.h"
@@ -64,11 +67,21 @@
int TEH_allow_keys_timetravel;
/**
+ * Should we allow two HTTPDs to bind to the same port?
+ */
+static int allow_address_reuse;
+
+/**
* The exchange's configuration (global)
*/
const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
/**
+ * Our KYC configuration.
+ */
+struct TEH_KycOptions TEH_kyc_config;
+
+/**
* How long is caching /keys allowed at most? (global)
*/
struct GNUNET_TIME_Relative TEH_max_keys_caching;
@@ -95,6 +108,11 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
char *TEH_currency;
/**
+ * Our base URL.
+ */
+char *TEH_base_url;
+
+/**
* Default timeout in seconds for HTTP requests.
*/
static unsigned int connection_timeout = 30;
@@ -126,6 +144,17 @@ static unsigned long long req_count;
*/
static unsigned long long req_max;
+/**
+ * Context for all CURL operations (useful to the event loop)
+ */
+struct GNUNET_CURL_Context *TEH_curl_ctx;
+
+/**
+ * Context for integrating #exchange_curl_ctx with the
+ * GNUnet event loop.
+ */
+static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
+
/**
* Signature of functions that handle operations on coins.
@@ -261,8 +290,10 @@ handle_mhd_completion_callback (void *cls,
return;
GNUNET_async_scope_enter (&rc->async_scope_id,
&old_scope);
+ TEH_check_invariants ();
if (NULL != rc->rh_cleaner)
rc->rh_cleaner (rc);
+ TEH_check_invariants ();
{
#if MHD_VERSION >= 0x00097304
const union MHD_ConnectionInfo *ci;
@@ -443,6 +474,7 @@ handler_seed (struct TEH_RequestContext *rc,
MHD_RESULT ret;
struct MHD_Response *resp;
+ (void) args;
body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */
if (NULL == body)
return MHD_NO;
@@ -516,7 +548,7 @@ handle_post_management (struct TEH_RequestContext *rc,
if (0 == strcmp (args[0],
"denominations"))
{
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
if ( (NULL == args[0]) ||
(NULL == args[1]) ||
@@ -622,12 +654,12 @@ handle_post_management (struct TEH_RequestContext *rc,
* Handle a get "/management" request.
*
* @param rc request context
- * @param args array of additional options (must be empty for this function)
+ * @param args array of additional options (must be [0] == "keys")
* @return MHD result code
*/
static MHD_RESULT
handle_get_management (struct TEH_RequestContext *rc,
- const char *const args[1])
+ const char *const args[2])
{
if ( (NULL != args[0]) &&
(0 == strcmp (args[0],
@@ -657,7 +689,7 @@ handle_post_auditors (struct TEH_RequestContext *rc,
const char *const args[])
{
struct TALER_AuditorPublicKeyP auditor_pub;
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
if ( (NULL == args[0]) ||
(NULL == args[1]) ||
@@ -827,6 +859,25 @@ handle_mhd_request (void *cls,
.handler.get = &TEH_handler_deposits_get,
.nargs = 4
},
+ /* KYC endpoints */
+ {
+ .url = "kyc-check",
+ .method = MHD_HTTP_METHOD_GET,
+ .handler.get = &TEH_handler_kyc_check,
+ .nargs = 1
+ },
+ {
+ .url = "kyc-proof",
+ .method = MHD_HTTP_METHOD_GET,
+ .handler.get = &TEH_handler_kyc_proof,
+ .nargs = 1
+ },
+ {
+ .url = "kyc-wallet",
+ .method = MHD_HTTP_METHOD_POST,
+ .handler.post = &TEH_handler_kyc_wallet,
+ .nargs = 0
+ },
/* POST management endpoints */
{
.url = "management",
@@ -882,6 +933,7 @@ handle_mhd_request (void *cls,
/* We're in a new async scope! */
rc = *con_cls = GNUNET_new (struct TEH_RequestContext);
GNUNET_async_scope_fresh (&rc->async_scope_id);
+ TEH_check_invariants ();
rc->url = url;
rc->connection = connection;
/* We only read the correlation ID on the first callback for every client */
@@ -900,6 +952,7 @@ handle_mhd_request (void *cls,
GNUNET_async_scope_enter (&rc->async_scope_id,
&old_scope);
+ TEH_check_invariants ();
if (NULL != correlation_id)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling request (%s) for URL '%s', correlation_id=%s\n",
@@ -1071,6 +1124,157 @@ handle_mhd_request (void *cls,
/**
+ * Load general KYC configuration parameters for the exchange server into the
+ * #TEH_kyc_config variable.
+ *
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_kyc_settings (void)
+{
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
+ "exchange",
+ "KYC_WITHDRAW_PERIOD",
+ &TEH_kyc_config.withdraw_period))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "KYC_WITHDRAW_PERIOD",
+ "valid relative time expected");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period))
+ return GNUNET_OK;
+ if (GNUNET_OK !=
+ TALER_config_get_amount (TEH_cfg,
+ "exchange",
+ "KYC_WITHDRAW_LIMIT",
+ &TEH_kyc_config.withdraw_limit))
+ return GNUNET_SYSERR;
+ if (0 != strcasecmp (TEH_kyc_config.withdraw_limit.currency,
+ TEH_currency))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "KYC_WITHDRAW_LIMIT",
+ "currency mismatch");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Load OAuth2.0 configuration parameters for the exchange server into the
+ * #TEH_kyc_config variable.
+ *
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_kyc_oauth_cfg (void)
+{
+ char *s;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_URL",
+ &s))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_URL");
+ return GNUNET_SYSERR;
+ }
+ if ( (! TALER_url_valid_charset (s)) ||
+ ( (0 != strncasecmp (s,
+ "http://",
+ strlen ("http://"))) &&
+ (0 != strncasecmp (s,
+ "https://",
+ strlen ("https://"))) ) )
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_URL",
+ "not a valid URL");
+ GNUNET_free (s);
+ return GNUNET_SYSERR;
+ }
+ TEH_kyc_config.details.oauth2.url = s;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange-kyc-oauth2",
+ "KYC_INFO_URL",
+ &s))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_INFO_URL");
+ return GNUNET_SYSERR;
+ }
+ if ( (! TALER_url_valid_charset (s)) ||
+ ( (0 != strncasecmp (s,
+ "http://",
+ strlen ("http://"))) &&
+ (0 != strncasecmp (s,
+ "https://",
+ strlen ("https://"))) ) )
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_INFO_URL",
+ "not a valid URL");
+ GNUNET_free (s);
+ return GNUNET_SYSERR;
+ }
+ TEH_kyc_config.details.oauth2.info_url = s;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_CLIENT_ID",
+ &s))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_CLIENT_ID");
+ return GNUNET_SYSERR;
+ }
+ TEH_kyc_config.details.oauth2.client_id = s;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_CLIENT_SECRET",
+ &s))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_CLIENT_SECRET");
+ return GNUNET_SYSERR;
+ }
+ TEH_kyc_config.details.oauth2.client_secret = s;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_POST_URL",
+ &s))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-kyc-oauth2",
+ "KYC_OAUTH2_POST_URL");
+ return GNUNET_SYSERR;
+ }
+ TEH_kyc_config.details.oauth2.post_kyc_redirect_url = s;
+ return GNUNET_OK;
+}
+
+
+/**
* Load configuration parameters for the exchange
* server into the corresponding global variables.
*
@@ -1079,6 +1283,47 @@ handle_mhd_request (void *cls,
static enum GNUNET_GenericReturnValue
exchange_serve_process_config (void)
{
+ {
+ char *kyc_mode;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange",
+ "KYC_MODE",
+ &kyc_mode))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "KYC_MODE");
+ return GNUNET_SYSERR;
+ }
+ if (0 == strcasecmp (kyc_mode,
+ "NONE"))
+ {
+ TEH_kyc_config.mode = TEH_KYC_NONE;
+ }
+ else if (0 == strcasecmp (kyc_mode,
+ "OAUTH2"))
+ {
+ TEH_kyc_config.mode = TEH_KYC_OAUTH2;
+ if (GNUNET_OK !=
+ parse_kyc_oauth_cfg ())
+ {
+ GNUNET_free (kyc_mode);
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "KYC_MODE",
+ "Must be 'NONE' or 'OAUTH2'");
+ GNUNET_free (kyc_mode);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (kyc_mode);
+ }
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
"exchange",
@@ -1123,6 +1368,55 @@ exchange_serve_process_config (void)
"CURRENCY");
return GNUNET_SYSERR;
}
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange",
+ "BASE_URL",
+ &TEH_base_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "BASE_URL");
+ return GNUNET_SYSERR;
+ }
+ if (! TALER_url_valid_charset (TEH_base_url))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "BASE_URL",
+ "invalid URL");
+ return GNUNET_SYSERR;
+ }
+
+ if (TEH_KYC_NONE != TEH_kyc_config.mode)
+ {
+ if (GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (TEH_cfg,
+ "exchange",
+ "KYC_WALLET_BALANCE_LIMIT"))
+ {
+ if ( (GNUNET_OK !=
+ TALER_config_get_amount (TEH_cfg,
+ "exchange",
+ "KYC_WALLET_BALANCE_LIMIT",
+ &TEH_kyc_config.wallet_balance_limit)) ||
+ (0 != strcasecmp (TEH_currency,
+ TEH_kyc_config.wallet_balance_limit.currency)) )
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "KYC_WALLET_BALANCE_LIMIT",
+ "valid amount expected");
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ memset (&TEH_kyc_config.wallet_balance_limit,
+ 0,
+ sizeof (TEH_kyc_config.wallet_balance_limit));
+ }
+ }
{
char *master_public_key_str;
@@ -1151,6 +1445,12 @@ exchange_serve_process_config (void)
}
GNUNET_free (master_public_key_str);
}
+ if (TEH_KYC_NONE != TEH_kyc_config.mode)
+ {
+ if (GNUNET_OK !=
+ parse_kyc_settings ())
+ return GNUNET_SYSERR;
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Launching exchange with public key `%s'...\n",
GNUNET_p2s (&TEH_master_public_key.eddsa_pub));
@@ -1380,11 +1680,27 @@ do_shutdown (void *cls)
mhd = TALER_MHD_daemon_stop ();
TEH_resume_keys_requests (true);
TEH_reserves_get_cleanup ();
+ TEH_kyc_check_cleanup ();
+ TEH_kyc_proof_cleanup ();
if (NULL != mhd)
MHD_stop_daemon (mhd);
TEH_WIRE_done ();
TEH_keys_finished ();
- TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
+ if (NULL != TEH_plugin)
+ {
+ TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
+ TEH_plugin = NULL;
+ }
+ if (NULL != TEH_curl_ctx)
+ {
+ GNUNET_CURL_fini (TEH_curl_ctx);
+ TEH_curl_ctx = NULL;
+ }
+ if (NULL != exchange_curl_rc)
+ {
+ GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc);
+ exchange_curl_rc = NULL;
+ }
}
@@ -1406,6 +1722,9 @@ run (void *cls,
enum TALER_MHD_GlobalOptions go;
int fh;
+ (void) cls;
+ (void) args;
+ (void ) cfgfile;
go = TALER_MHD_GO_NONE;
if (connection_close)
go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
@@ -1442,6 +1761,17 @@ run (void *cls,
}
TEH_load_terms (TEH_cfg);
+ TEH_curl_ctx
+ = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &exchange_curl_rc);
+ if (NULL == TEH_curl_ctx)
+ {
+ GNUNET_break (0);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
fh = TALER_MHD_bind (TEH_cfg,
@@ -1478,6 +1808,10 @@ run (void *cls,
NULL,
MHD_OPTION_CONNECTION_TIMEOUT,
connection_timeout,
+ (0 == allow_address_reuse)
+ ? MHD_OPTION_END
+ : MHD_OPTION_LISTENING_ADDRESS_REUSE,
+ (unsigned int) allow_address_reuse,
MHD_OPTION_END);
if (NULL == mhd)
{
@@ -1518,6 +1852,10 @@ main (int argc,
"connection-close",
"force HTTP connections to be closed after each request",
&connection_close),
+ GNUNET_GETOPT_option_flag ('r',
+ "allow-reuse-address",
+ "allow multiple HTTPDs to listen to the same port",
+ &allow_address_reuse),
GNUNET_GETOPT_option_uint ('t',
"timeout",
"SECONDS",
@@ -1534,7 +1872,6 @@ main (int argc,
#endif
GNUNET_GETOPT_option_help (
"HTTP server providing a RESTful API to access a Taler exchange"),
- GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
GNUNET_GETOPT_OPTION_END
};
enum GNUNET_GenericReturnValue ret;
diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h
index e4342648..d52ce1a0 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -30,6 +30,101 @@
/**
+ * Enumeration for our KYC modes.
+ */
+enum TEH_KycMode
+{
+ /**
+ * KYC is disabled.
+ */
+ TEH_KYC_NONE = 0,
+
+ /**
+ * We use Oauth2.0.
+ */
+ TEH_KYC_OAUTH2 = 1
+};
+
+
+/**
+ * Structure describing our KYC configuration.
+ */
+struct TEH_KycOptions
+{
+ /**
+ * What KYC mode are we in?
+ */
+ enum TEH_KycMode mode;
+
+ /**
+ * Maximum amount that can be withdrawn in @e withdraw_period without
+ * needing KYC.
+ * Only valid if @e mode is not #TEH_KYC_NONE and
+ * if @e withdraw_period is non-zero.
+ */
+ struct TALER_Amount withdraw_limit;
+
+ /**
+ * Maximum balance a wallet can hold without
+ * needing KYC.
+ * Only valid if @e mode is not #TEH_KYC_NONE and
+ * if the amount specified is valid.
+ */
+ struct TALER_Amount wallet_balance_limit;
+
+ /**
+ * Time period over which @e withdraw_limit applies.
+ * Only valid if @e mode is not #TEH_KYC_NONE.
+ */
+ struct GNUNET_TIME_Relative withdraw_period;
+
+ /**
+ * Details depending on @e mode.
+ */
+ union
+ {
+
+ /**
+ * Configuration details if @e mode is #TEH_KYC_OAUTH2.
+ */
+ struct
+ {
+
+ /**
+ * URL of the OAuth2.0 endpoint for KYC checks.
+ */
+ char *url;
+
+ /**
+ * URL of the user info access endpoint.
+ */
+ char *info_url;
+
+ /**
+ * Our client ID for OAuth2.0.
+ */
+ char *client_id;
+
+ /**
+ * Our client secret for OAuth2.0.
+ */
+ char *client_secret;
+
+ /**
+ * Where to redirect clients after the
+ * Web-based KYC process is done?
+ */
+ char *post_kyc_redirect_url;
+
+ } oauth2;
+
+ } details;
+};
+
+
+extern struct TEH_KycOptions TEH_kyc_config;
+
+/**
* How long is caching /keys allowed at most?
*/
extern struct GNUNET_TIME_Relative TEH_max_keys_caching;
@@ -79,10 +174,19 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
extern char *TEH_currency;
/**
+ * Our (externally visible) base URL.
+ */
+extern char *TEH_base_url;
+
+/**
* Are we shutting down?
*/
extern volatile bool MHD_terminating;
+/**
+ * Context for all CURL operations (useful to the event loop)
+ */
+extern struct GNUNET_CURL_Context *TEH_curl_ctx;
/**
* @brief Struct describing an URL and the handler for it.
diff --git a/src/exchange/taler-exchange-httpd_auditors.c b/src/exchange/taler-exchange-httpd_auditors.c
index 34e640ad..bf4a9b2c 100644
--- a/src/exchange/taler-exchange-httpd_auditors.c
+++ b/src/exchange/taler-exchange-httpd_auditors.c
@@ -45,7 +45,7 @@ struct AddAuditorDenomContext
/**
* Denomination this is about.
*/
- const struct GNUNET_HashCode *h_denom_pub;
+ const struct TALER_DenominationHash *h_denom_pub;
/**
* Auditor this is about.
@@ -101,7 +101,7 @@ add_auditor_denom_sig (void *cls,
connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
- GNUNET_h2s (awc->h_denom_pub));
+ GNUNET_h2s (&awc->h_denom_pub->hash));
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -192,7 +192,7 @@ MHD_RESULT
TEH_handler_auditors (
struct MHD_Connection *connection,
const struct TALER_AuditorPublicKeyP *auditor_pub,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHash *h_denom_pub,
const json_t *root)
{
struct AddAuditorDenomContext awc = {
diff --git a/src/exchange/taler-exchange-httpd_auditors.h b/src/exchange/taler-exchange-httpd_auditors.h
index f8191f8a..00a2e57a 100644
--- a/src/exchange/taler-exchange-httpd_auditors.h
+++ b/src/exchange/taler-exchange-httpd_auditors.h
@@ -39,7 +39,7 @@ MHD_RESULT
TEH_handler_auditors (
struct MHD_Connection *connection,
const struct TALER_AuditorPublicKeyP *auditor_pub,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHash *h_denom_pub,
const json_t *root);
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index 3c693649..1d78fb8e 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -182,7 +182,7 @@ TEH_DB_run_transaction (struct MHD_Connection *connection,
}
/* make sure callback did not violate invariants! */
GNUNET_assert ( (NULL == mhd_ret) ||
- (-1 == *mhd_ret) );
+ (-1 == (int) *mhd_ret) );
if (0 <= qs)
return GNUNET_OK;
}
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 9c4cfdc7..e9851de7 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-2020 Taler Systems SA
+ Copyright (C) 2014-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -57,10 +57,12 @@
static MHD_RESULT
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,
+ const struct TALER_MerchantWireHash *h_wire,
+ const struct TALER_ExtensionContractHash *h_extensions,
+ const struct TALER_PrivateContractHash *h_contract_terms,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
+ struct GNUNET_TIME_Absolute wire_deadline,
const struct TALER_MerchantPublicKeyP *merchant,
const struct TALER_Amount *amount_without_fee)
{
@@ -73,11 +75,14 @@ reply_deposit_success (struct MHD_Connection *connection,
.h_wire = *h_wire,
.exchange_timestamp = GNUNET_TIME_absolute_hton (exchange_timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
+ .wire_deadline = GNUNET_TIME_absolute_hton (wire_deadline),
.coin_pub = *coin_pub,
- .merchant = *merchant
+ .merchant_pub = *merchant
};
enum TALER_ErrorCode ec;
+ if (NULL != h_extensions)
+ dc.h_extensions = *h_extensions;
TALER_amount_hton (&dc.amount_without_fee,
amount_without_fee);
if (TALER_EC_NONE !=
@@ -117,10 +122,19 @@ struct DepositContext
struct GNUNET_TIME_Absolute exchange_timestamp;
/**
+ * Calculated hash over the wire details.
+ */
+ struct TALER_MerchantWireHash h_wire;
+
+ /**
* Value of the coin.
*/
struct TALER_Amount value;
+ /**
+ * payto:// URI of the credited account.
+ */
+ const char *payto_uri;
};
@@ -148,7 +162,6 @@ deposit_precheck (void *cls,
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
deposit,
- GNUNET_YES /* check refund deadline */,
&deposit_fee,
&dc->exchange_timestamp);
if (qs < 0)
@@ -158,7 +171,7 @@ deposit_precheck (void *cls,
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL);
+ "have_deposit");
return GNUNET_DB_STATUS_HARD_ERROR;
}
return qs;
@@ -175,10 +188,12 @@ deposit_precheck (void *cls,
&deposit_fee));
*mhd_ret = reply_deposit_success (connection,
&deposit->coin.coin_pub,
- &deposit->h_wire,
+ &dc->h_wire,
+ NULL /* h_extensions! */,
&deposit->h_contract_terms,
dc->exchange_timestamp,
deposit->refund_deadline,
+ deposit->wire_deadline,
&deposit->merchant_pub,
&amount_without_fee);
/* Treat as 'hard' DB error as we want to rollback and
@@ -218,7 +233,6 @@ deposit_transaction (void *cls,
mhd_ret);
if (qs < 0)
return qs;
-
/* Theoretically, someone other threat may have received
and committed the deposit in the meantime. Check now
that we are in the transaction scope. */
@@ -315,25 +329,24 @@ TEH_handler_deposit (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const json_t *root)
{
- json_t *wire;
struct DepositContext dc;
struct TALER_EXCHANGEDB_Deposit deposit;
- struct GNUNET_HashCode my_h_wire;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("wire", &wire),
+ GNUNET_JSON_spec_string ("merchant_payto_uri",
+ &dc.payto_uri),
+ GNUNET_JSON_spec_fixed_auto ("wire_salt",
+ &deposit.wire_salt),
TALER_JSON_spec_amount ("contribution",
TEH_currency,
&deposit.amount_with_fee),
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&deposit.coin.denom_pub_hash),
- TALER_JSON_spec_denomination_signature ("ub_sig",
- &deposit.coin.denom_sig),
+ TALER_JSON_spec_denom_sig ("ub_sig",
+ &deposit.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&deposit.merchant_pub),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&deposit.h_contract_terms),
- GNUNET_JSON_spec_fixed_auto ("h_wire",
- &deposit.h_wire),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&deposit.csig),
TALER_JSON_spec_absolute_time ("timestamp",
@@ -351,7 +364,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
sizeof (deposit));
deposit.coin.coin_pub = *coin_pub;
{
- int res;
+ enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (connection,
root,
@@ -369,21 +382,9 @@ TEH_handler_deposit (struct MHD_Connection *connection,
}
/* validate merchant's wire details (as far as we can) */
{
- char *payto;
char *emsg;
- payto = TALER_JSON_wire_to_payto (wire);
- if (NULL == payto)
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "wire");
- }
- emsg = TALER_payto_validate (payto);
- GNUNET_free (payto);
+ emsg = TALER_payto_validate (dc.payto_uri);
if (NULL != emsg)
{
MHD_RESULT ret;
@@ -398,7 +399,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
return ret;
}
}
- deposit.receiver_wire_account = wire;
+ deposit.receiver_wire_account = (char *) dc.payto_uri;
if (deposit.refund_deadline.abs_value_us > deposit.wire_deadline.abs_value_us)
{
GNUNET_break_op (0);
@@ -408,29 +409,9 @@ TEH_handler_deposit (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
NULL);
}
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (wire,
- &my_h_wire))
- {
- TALER_LOG_WARNING (
- "Failed to parse JSON wire format specification for /deposit request\n");
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSIT_INVALID_WIRE_FORMAT_JSON,
- NULL);
- }
- if (0 != GNUNET_memcmp (&deposit.h_wire,
- &my_h_wire))
- {
- /* Client hashed wire details differently than we did, reject */
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSIT_INVALID_WIRE_FORMAT_CONTRACT_HASH_CONFLICT,
- NULL);
- }
-
+ TALER_merchant_wire_signature_hash (dc.payto_uri,
+ &deposit.wire_salt,
+ &dc.h_wire);
/* Check for idempotency: did we get this request before? */
dc.deposit = &deposit;
{
@@ -536,37 +517,25 @@ TEH_handler_deposit (struct MHD_Connection *connection,
NULL);
}
- /* check deposit signature */
+ if (GNUNET_OK !=
+ TALER_wallet_deposit_verify (&deposit.amount_with_fee,
+ &deposit.deposit_fee,
+ &dc.h_wire,
+ &deposit.h_contract_terms,
+ NULL /* h_extensions! */,
+ &deposit.coin.denom_pub_hash,
+ deposit.timestamp,
+ &deposit.merchant_pub,
+ deposit.refund_deadline,
+ &deposit.coin.coin_pub,
+ &deposit.csig))
{
- struct TALER_DepositRequestPS dr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
- .purpose.size = htonl (sizeof (dr)),
- .h_contract_terms = deposit.h_contract_terms,
- .h_wire = deposit.h_wire,
- .h_denom_pub = deposit.coin.denom_pub_hash,
- .wallet_timestamp = GNUNET_TIME_absolute_hton (deposit.timestamp),
- .refund_deadline = GNUNET_TIME_absolute_hton (deposit.refund_deadline),
- .merchant = deposit.merchant_pub,
- .coin_pub = deposit.coin.coin_pub
- };
-
- TALER_amount_hton (&dr.amount_with_fee,
- &deposit.amount_with_fee);
- TALER_amount_hton (&dr.deposit_fee,
- &deposit.deposit_fee);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
- &dr,
- &deposit.csig.eddsa_signature,
- &deposit.coin.coin_pub.eddsa_pub))
- {
- TALER_LOG_WARNING ("Invalid signature on /deposit request\n");
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_UNAUTHORIZED,
- TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
- NULL);
- }
+ TALER_LOG_WARNING ("Invalid signature on /deposit request\n");
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
+ NULL);
}
/* execute transaction */
@@ -596,10 +565,12 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.deposit_fee));
res = reply_deposit_success (connection,
&deposit.coin.coin_pub,
- &deposit.h_wire,
+ &dc.h_wire,
+ NULL /* h_extensions! */,
&deposit.h_contract_terms,
dc.exchange_timestamp,
deposit.refund_deadline,
+ deposit.wire_deadline,
&deposit.merchant_pub,
&amount_without_fee);
GNUNET_JSON_parse_free (spec);
diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c b/src/exchange/taler-exchange-httpd_deposits_get.c
index 04e2acb0..d981a8dd 100644
--- a/src/exchange/taler-exchange-httpd_deposits_get.c
+++ b/src/exchange/taler-exchange-httpd_deposits_get.c
@@ -47,8 +47,8 @@
*/
static MHD_RESULT
reply_deposit_details (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *h_contract_terms,
- const struct GNUNET_HashCode *h_wire,
+ const struct TALER_PrivateContractHash *h_contract_terms,
+ const struct TALER_MerchantWireHash *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_contribution,
const struct TALER_WireTransferIdentifierRawP *wtid,
@@ -137,61 +137,20 @@ struct DepositWtidContext
struct TALER_Amount coin_delta;
/**
+ * KYC status information for the receiving account.
+ */
+ struct TALER_EXCHANGEDB_KycStatus kyc;
+
+ /**
* 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;
+ enum GNUNET_GenericReturnValue pending;
};
/**
- * Function called with the results of the lookup of the
- * wire transfer identifier information.
- *
- * @param cls our context for transmission, a `struct DepositWtidContext *`
- * @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 (0 >
- 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 "deposits" GET. Returns the transfer information
* associated with the given deposit.
*
@@ -214,14 +173,21 @@ deposits_get_transaction (void *cls,
{
struct DepositWtidContext *ctx = cls;
enum GNUNET_DB_QueryStatus qs;
+ bool pending;
+ struct TALER_Amount fee;
qs = TEH_plugin->lookup_transfer_by_deposit (TEH_plugin->cls,
&ctx->tps->h_contract_terms,
&ctx->tps->h_wire,
&ctx->tps->coin_pub,
ctx->merchant_pub,
- &handle_wtid_data,
- ctx);
+
+ &pending,
+ &ctx->wtid,
+ &ctx->execution_time,
+ &ctx->coin_contribution,
+ &fee,
+ &ctx->kyc);
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@@ -242,6 +208,17 @@ deposits_get_transaction (void *cls,
NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+
+ if (0 >
+ TALER_amount_subtract (&ctx->coin_delta,
+ &ctx->coin_contribution,
+ &fee))
+ {
+ GNUNET_break (0);
+ ctx->pending = GNUNET_SYSERR;
+ return qs;
+ }
+ ctx->pending = (pending) ? GNUNET_YES : GNUNET_NO;
return qs;
}
@@ -262,7 +239,6 @@ handle_track_transaction_request (
{
MHD_RESULT mhd_ret;
struct DepositWtidContext ctx = {
- .pending = GNUNET_NO,
.tps = tps,
.merchant_pub = merchant_pub
};
@@ -274,17 +250,21 @@ handle_track_transaction_request (
&deposits_get_transaction,
&ctx))
return mhd_ret;
+ if (GNUNET_SYSERR == ctx.pending)
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ "wire fees exceed aggregate in database");
if (GNUNET_YES == ctx.pending)
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_ACCEPTED,
+ GNUNET_JSON_pack_uint64 ("payment_target_uuid",
+ ctx.kyc.payment_target_uuid),
+ GNUNET_JSON_pack_bool ("kyc_ok",
+ ctx.kyc.ok),
GNUNET_JSON_pack_time_abs ("execution_time",
ctx.execution_time));
- if (GNUNET_SYSERR == ctx.pending)
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
- "wire fees exceed aggregate in database");
return reply_deposit_details (connection,
&tps->h_contract_terms,
&tps->h_wire,
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 72c2de12..b7359392 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -26,6 +26,7 @@
#include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_responses.h"
#include "taler_exchangedb_plugin.h"
+#include "taler_extensions.h"
/**
@@ -50,7 +51,7 @@
* #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
* exchange_api_handle.c!
*/
-#define EXCHANGE_PROTOCOL_VERSION "9:0:0"
+#define EXCHANGE_PROTOCOL_VERSION "10:0:0"
/**
@@ -71,9 +72,9 @@ struct HelperDenomination
struct GNUNET_TIME_Relative validity_duration;
/**
- * Hash of the denomination key.
+ * Hash of the full denomination key.
*/
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
/**
* Signature over this key from the security module's key.
@@ -86,10 +87,24 @@ struct HelperDenomination
struct TALER_DenominationPublicKey denom_pub;
/**
+ * Details depend on the @e denom_pub.cipher type.
+ */
+ union
+ {
+
+ /**
+ * Hash of the RSA key.
+ */
+ struct TALER_RsaPubHashP h_rsa;
+
+ } h_details;
+
+ /**
* Name in configuration section for this denomination type.
*/
char *section_name;
+
};
@@ -167,7 +182,7 @@ struct HelperState
/**
* Handle for the denom/RSA helper.
*/
- struct TALER_CRYPTO_DenominationHelper *dh;
+ struct TALER_CRYPTO_RsaDenominationHelper *dh;
/**
* Map from H(denom_pub) to `struct HelperDenomination` entries.
@@ -175,6 +190,11 @@ struct HelperState
struct GNUNET_CONTAINER_MultiHashMap *denom_keys;
/**
+ * Map from H(rsa_pub) to `struct HelperDenomination` entries.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *rsa_keys;
+
+ /**
* Map from `struct TALER_ExchangePublicKey` to `struct HelperSignkey`
* entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also
* an EdDSA public key.
@@ -442,6 +462,44 @@ suspend_request (struct MHD_Connection *connection)
}
+/**
+ * Called on each denomination key. Checks that the key still works.
+ *
+ * @param cls NULL
+ * @param hc denomination hash (unused)
+ * @param value a `struct TEH_DenominationKey`
+ * @return #GNUNET_OK
+ */
+static int
+check_dk (void *cls,
+ const struct GNUNET_HashCode *hc,
+ void *value)
+{
+ struct TEH_DenominationKey *dk = value;
+
+
+ (void) hc;
+ (void) value;
+ GNUNET_assert (TALER_DENOMINATION_INVALID != dk->denom_pub.cipher);
+ if (TALER_DENOMINATION_RSA == dk->denom_pub.cipher)
+ GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check (
+ dk->denom_pub.details.rsa_public_key));
+ return GNUNET_OK;
+}
+
+
+void
+TEH_check_invariants ()
+{
+ struct TEH_KeyStateHandle *ksh;
+
+ ksh = TEH_keys_get_state ();
+ GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
+ &check_dk,
+ NULL);
+}
+
+
void
TEH_resume_keys_requests (bool do_shutdown)
{
@@ -549,7 +607,7 @@ free_denom_cb (void *cls,
(void) cls;
(void) h_denom_pub;
- GNUNET_CRYPTO_rsa_public_key_free (hd->denom_pub.rsa_public_key);
+ TALER_denom_pub_free (&hd->denom_pub);
GNUNET_free (hd->section_name);
GNUNET_free (hd);
return GNUNET_OK;
@@ -591,6 +649,8 @@ destroy_key_helpers (struct HelperState *hs)
GNUNET_CONTAINER_multihashmap_iterate (hs->denom_keys,
&free_denom_cb,
hs);
+ GNUNET_CONTAINER_multihashmap_destroy (hs->rsa_keys);
+ hs->rsa_keys = NULL;
GNUNET_CONTAINER_multihashmap_destroy (hs->denom_keys);
hs->denom_keys = NULL;
GNUNET_CONTAINER_multipeermap_iterate (hs->esign_keys,
@@ -600,7 +660,7 @@ destroy_key_helpers (struct HelperState *hs)
hs->esign_keys = NULL;
if (NULL != hs->dh)
{
- TALER_CRYPTO_helper_denom_disconnect (hs->dh);
+ TALER_CRYPTO_helper_rsa_disconnect (hs->dh);
hs->dh = NULL;
}
if (NULL != hs->esh)
@@ -628,31 +688,33 @@ destroy_key_helpers (struct HelperState *hs)
* @param sm_pub public key of the security module, NULL if the key was revoked or purged
* @param sm_sig signature from the security module, NULL if the key was revoked or purged
* The signature was already verified against @a sm_pub.
+ * @param age_restricted true, if denomination is age restricted
*/
static void
-helper_denom_cb (
+helper_rsa_cb (
void *cls,
const char *section_name,
struct GNUNET_TIME_Absolute start_time,
struct GNUNET_TIME_Relative validity_duration,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_RsaPubHashP *h_rsa,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_SecurityModulePublicKeyP *sm_pub,
- const struct TALER_SecurityModuleSignatureP *sm_sig)
+ const struct TALER_SecurityModuleSignatureP *sm_sig,
+ bool age_restricted)
{
struct HelperState *hs = cls;
struct HelperDenomination *hd;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"RSA helper announces key %s for denomination type %s with validity %s\n",
- GNUNET_h2s (h_denom_pub),
+ GNUNET_h2s (&h_rsa->hash),
section_name,
GNUNET_STRINGS_relative_time_to_string (validity_duration,
GNUNET_NO));
key_generation++;
TEH_resume_keys_requests (false);
- hd = GNUNET_CONTAINER_multihashmap_get (hs->denom_keys,
- h_denom_pub);
+ hd = GNUNET_CONTAINER_multihashmap_get (hs->rsa_keys,
+ &h_rsa->hash);
if (NULL != hd)
{
/* should be just an update (revocation!), so update existing entry */
@@ -664,16 +726,38 @@ helper_denom_cb (
hd = GNUNET_new (struct HelperDenomination);
hd->start_time = start_time;
hd->validity_duration = validity_duration;
- hd->h_denom_pub = *h_denom_pub;
+ hd->h_details.h_rsa = *h_rsa;
hd->sm_sig = *sm_sig;
- hd->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (denom_pub->rsa_public_key);
+ GNUNET_assert (TALER_DENOMINATION_RSA == denom_pub->cipher);
+ TALER_denom_pub_deep_copy (&hd->denom_pub,
+ denom_pub);
+ GNUNET_assert (TALER_DENOMINATION_RSA == hd->denom_pub.cipher);
+
+ /* Set age restriction, if applicable */
+ hd->denom_pub.age_mask.mask = 0;
+ if (age_restricted)
+ {
+ /* FIXME-oec: get age mask from global */
+ GNUNET_assert (TALER_EXTENSION_OK == TALER_get_age_mask (TEH_cfg,
+ &hd->denom_pub.
+ age_mask));
+ }
+
+ TALER_denom_pub_hash (&hd->denom_pub,
+ &hd->h_denom_pub);
hd->section_name = GNUNET_strdup (section_name);
GNUNET_assert (
GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (
hs->denom_keys,
- &hd->h_denom_pub,
+ &hd->h_denom_pub.hash,
+ hd,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_assert (
+ GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (
+ hs->rsa_keys,
+ &hd->h_details.h_rsa.hash,
hd,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@@ -721,7 +805,6 @@ helper_esign_cb (
{
/* should be just an update (revocation!), so update existing entry */
hsk->validity_duration = validity_duration;
- GNUNET_break (0 == start_time.abs_value_us);
return;
}
GNUNET_assert (NULL != sm_pub);
@@ -753,12 +836,15 @@ setup_key_helpers (struct HelperState *hs)
hs->denom_keys
= GNUNET_CONTAINER_multihashmap_create (1024,
GNUNET_YES);
+ hs->rsa_keys
+ = GNUNET_CONTAINER_multihashmap_create (1024,
+ GNUNET_YES);
hs->esign_keys
= GNUNET_CONTAINER_multipeermap_create (32,
GNUNET_NO /* MUST BE NO! */);
- hs->dh = TALER_CRYPTO_helper_denom_connect (TEH_cfg,
- &helper_denom_cb,
- hs);
+ hs->dh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg,
+ &helper_rsa_cb,
+ hs);
if (NULL == hs->dh)
{
destroy_key_helpers (hs);
@@ -784,7 +870,7 @@ setup_key_helpers (struct HelperState *hs)
static void
sync_key_helpers (struct HelperState *hs)
{
- TALER_CRYPTO_helper_denom_poll (hs->dh);
+ TALER_CRYPTO_helper_rsa_poll (hs->dh);
TALER_CRYPTO_helper_esign_poll (hs->esh);
}
@@ -807,7 +893,7 @@ clear_denomination_cb (void *cls,
(void) cls;
(void) h_denom_pub;
- GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub.rsa_public_key);
+ TALER_denom_pub_free (&dk->denom_pub);
while (NULL != (as = dk->as_head))
{
GNUNET_CONTAINER_DLL_remove (dk->as_head,
@@ -894,8 +980,12 @@ keys_update_event_cb (void *cls,
(void) cls;
(void) extra;
(void) extra_size;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Received /keys update event\n");
+ TEH_check_invariants ();
key_generation++;
TEH_resume_keys_requests (false);
+ TEH_check_invariants ();
}
@@ -965,7 +1055,7 @@ static void
denomination_info_cb (
void *cls,
const struct TALER_DenominationPublicKey *denom_pub,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta,
const struct TALER_MasterSignatureP *master_sig,
bool recoup_possible)
@@ -973,6 +1063,7 @@ denomination_info_cb (
struct TEH_KeyStateHandle *ksh = cls;
struct TEH_DenominationKey *dk;
+ GNUNET_assert (TALER_DENOMINATION_INVALID != denom_pub->cipher);
if ( (0 == meta->start.abs_value_us) ||
(0 == meta->expire_withdraw.abs_value_us) ||
(0 == meta->expire_deposit.abs_value_us) ||
@@ -980,12 +1071,12 @@ denomination_info_cb (
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Database contains invalid denomination key %s\n",
- GNUNET_h2s (h_denom_pub));
+ GNUNET_h2s (&h_denom_pub->hash));
return;
}
dk = GNUNET_new (struct TEH_DenominationKey);
- dk->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (denom_pub->rsa_public_key);
+ TALER_denom_pub_deep_copy (&dk->denom_pub,
+ denom_pub);
dk->h_denom_pub = *h_denom_pub;
dk->meta = *meta;
dk->master_sig = *master_sig;
@@ -993,7 +1084,7 @@ denomination_info_cb (
GNUNET_assert (
GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map,
- &dk->h_denom_pub,
+ &dk->h_denom_pub.hash,
dk,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@@ -1139,7 +1230,7 @@ static void
auditor_denom_cb (
void *cls,
const struct TALER_AuditorPublicKeyP *auditor_pub,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_AuditorSignatureP *auditor_sig)
{
struct TEH_KeyStateHandle *ksh = cls;
@@ -1147,7 +1238,7 @@ auditor_denom_cb (
struct TEH_AuditorSignature *as;
dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
- h_denom_pub);
+ &h_denom_pub->hash);
if (NULL == dk)
{
/* Odd, this should be impossible as per foreign key
@@ -1199,6 +1290,7 @@ add_sign_key_cb (void *cls,
struct SignKeyCtx *ctx = cls;
struct SigningKey *sk = value;
+ (void) pid;
ctx->next_sk_expire =
GNUNET_TIME_absolute_min (ctx->next_sk_expire,
sk->meta.expire_sign);
@@ -1345,7 +1437,7 @@ get_date_string (struct GNUNET_TIME_Absolute at,
* @param[in,out] response the response to modify
* @return #GNUNET_OK on success
*/
-static int
+static enum GNUNET_GenericReturnValue
setup_general_response_headers (const struct TEH_KeyStateHandle *ksh,
struct MHD_Response *response)
{
@@ -1473,6 +1565,18 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_JSON_pack_data_auto ("eddsa_sig",
&exchange_sig));
GNUNET_assert (NULL != keys);
+ if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
+ (GNUNET_OK ==
+ TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) )
+ {
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (
+ keys,
+ "wallet_balance_limit_without_kyc",
+ TALER_JSON_from_amount (
+ &TEH_kyc_config.wallet_balance_limit)));
+ }
{
char *keys_json;
@@ -1627,8 +1731,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
dk->meta.expire_deposit),
GNUNET_JSON_pack_time_abs ("stamp_expire_legal",
dk->meta.expire_legal),
- GNUNET_JSON_pack_rsa_public_key ("denom_pub",
- dk->denom_pub.rsa_public_key),
+ TALER_JSON_pack_denom_pub ("denom_pub",
+ &dk->denom_pub),
TALER_JSON_pack_amount ("value",
&dk->meta.value),
TALER_JSON_pack_amount ("fee_withdraw",
@@ -1863,7 +1967,7 @@ TEH_keys_get_state (void)
struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
+TEH_keys_denomination_by_hash (const struct TALER_DenominationHash *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret)
{
@@ -1886,15 +1990,16 @@ TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
- const struct GNUNET_HashCode *h_denom_pub,
- struct MHD_Connection *conn,
- MHD_RESULT *mret)
+TEH_keys_denomination_by_hash2 (
+ struct TEH_KeyStateHandle *ksh,
+ const struct TALER_DenominationHash *h_denom_pub,
+ struct MHD_Connection *conn,
+ MHD_RESULT *mret)
{
struct TEH_DenominationKey *dk;
dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
- h_denom_pub);
+ &h_denom_pub->hash);
if (NULL == dk)
{
*mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
@@ -1905,33 +2010,52 @@ TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
}
-struct TALER_DenominationSignature
-TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub,
+struct TALER_BlindedDenominationSignature
+TEH_keys_denomination_sign (const struct TALER_DenominationHash *h_denom_pub,
const void *msg,
size_t msg_size,
enum TALER_ErrorCode *ec)
{
struct TEH_KeyStateHandle *ksh;
- struct TALER_DenominationSignature none = { NULL };
+ struct TALER_BlindedDenominationSignature none;
+ struct HelperDenomination *hd;
+ memset (&none,
+ 0,
+ sizeof (none));
ksh = TEH_keys_get_state ();
if (NULL == ksh)
{
*ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
return none;
}
- return TALER_CRYPTO_helper_denom_sign (ksh->helpers->dh,
- h_denom_pub,
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &h_denom_pub->hash);
+ if (NULL == hd)
+ {
+ *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
+ return none;
+ }
+ switch (hd->denom_pub.cipher)
+ {
+ case TALER_DENOMINATION_RSA:
+ return TALER_CRYPTO_helper_rsa_sign (ksh->helpers->dh,
+ &hd->h_details.h_rsa,
msg,
msg_size,
ec);
+ default:
+ *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ return none;
+ }
}
void
-TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub)
+TEH_keys_denomination_revoke (const struct TALER_DenominationHash *h_denom_pub)
{
struct TEH_KeyStateHandle *ksh;
+ struct HelperDenomination *hd;
ksh = TEH_keys_get_state ();
if (NULL == ksh)
@@ -1939,9 +2063,24 @@ TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub)
GNUNET_break (0);
return;
}
- TALER_CRYPTO_helper_denom_revoke (ksh->helpers->dh,
- h_denom_pub);
- TEH_keys_update_states ();
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &h_denom_pub->hash);
+ if (NULL == hd)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ switch (hd->denom_pub.cipher)
+ {
+ case TALER_DENOMINATION_RSA:
+ TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->dh,
+ &hd->h_details.h_rsa);
+ TEH_keys_update_states ();
+ return;
+ default:
+ GNUNET_break (0);
+ return;
+ }
}
@@ -2272,7 +2411,7 @@ load_fees (const char *section_name,
enum GNUNET_GenericReturnValue
-TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
+TEH_keys_load_fees (const struct TALER_DenominationHash *h_denom_pub,
struct TALER_DenominationPublicKey *denom_pub,
struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
{
@@ -2288,12 +2427,12 @@ TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
}
hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
- h_denom_pub);
+ &h_denom_pub->hash);
if (NULL == hd)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Denomination %s not known\n",
- GNUNET_h2s (h_denom_pub));
+ GNUNET_h2s (&h_denom_pub->hash));
return GNUNET_NO;
}
meta->start = hd->start_time;
@@ -2302,15 +2441,25 @@ TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
ok = load_fees (hd->section_name,
meta);
if (GNUNET_OK == ok)
- denom_pub->rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (hd->denom_pub.rsa_public_key);
+ {
+ GNUNET_assert (TALER_DENOMINATION_INVALID != hd->denom_pub.cipher);
+ TALER_denom_pub_deep_copy (denom_pub,
+ &hd->denom_pub);
+ }
else
- denom_pub->rsa_public_key = NULL;
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No fees for `%s', voiding key\n",
+ hd->section_name);
+ memset (denom_pub,
+ 0,
+ sizeof (*denom_pub));
+ }
return ok;
}
-int
+enum GNUNET_GenericReturnValue
TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
struct TALER_EXCHANGEDB_SignkeyMetaData *meta)
{
@@ -2412,8 +2561,8 @@ add_future_denomkey_cb (void *cls,
meta.expire_deposit),
GNUNET_JSON_pack_time_abs ("stamp_expire_legal",
meta.expire_legal),
- GNUNET_JSON_pack_rsa_public_key ("denom_pub",
- hd->denom_pub.rsa_public_key),
+ TALER_JSON_pack_denom_pub ("denom_pub",
+ &hd->denom_pub),
TALER_JSON_pack_amount ("fee_withdraw",
&meta.fee_withdraw),
TALER_JSON_pack_amount ("fee_deposit",
@@ -2487,6 +2636,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
struct TEH_KeyStateHandle *ksh;
json_t *reply;
+ (void) rh;
ksh = get_key_state (true);
if (NULL == ksh)
{
@@ -2504,6 +2654,21 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
.signkeys = json_array ()
};
+ if (GNUNET_is_zero (&denom_sm_pub))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_GATEWAY,
+ TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE,
+ NULL);
+ }
+ if (GNUNET_is_zero (&esign_sm_pub))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_GATEWAY,
+ TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE,
+ NULL);
+ }
+ // then a secmod helper is not yet running and we should return an MHD_HTTP_BAD_GATEWAY!
GNUNET_assert (NULL != fbc.denoms);
GNUNET_assert (NULL != fbc.signkeys);
GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys,
diff --git a/src/exchange/taler-exchange-httpd_keys.h b/src/exchange/taler-exchange-httpd_keys.h
index ce5e2b73..75de7cba 100644
--- a/src/exchange/taler-exchange-httpd_keys.h
+++ b/src/exchange/taler-exchange-httpd_keys.h
@@ -50,7 +50,7 @@ struct TEH_DenominationKey
/**
* Hash code of the denomination public key.
*/
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
/**
* Meta data about the type of the denomination, such as fees and validity
@@ -97,6 +97,13 @@ struct TEH_KeyStateHandle;
/**
+ * Run internal invariant checks. For debugging.
+ */
+void
+TEH_check_invariants (void);
+
+
+/**
* Return the current key state for this thread. Possibly re-builds the key
* state if we have reason to believe that something changed.
*
@@ -135,7 +142,7 @@ TEH_keys_update_states (void);
* or NULL if @a h_denom_pub could not be found
*/
struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
+TEH_keys_denomination_by_hash (const struct TALER_DenominationHash *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret);
@@ -155,7 +162,8 @@ TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
*/
struct TEH_DenominationKey *
TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct
+ TALER_DenominationHash *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret);
@@ -170,8 +178,8 @@ TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
* @return signature, the value inside the structure will be NULL on failure,
* see @a ec for details about the failure
*/
-struct TALER_DenominationSignature
-TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub,
+struct TALER_BlindedDenominationSignature
+TEH_keys_denomination_sign (const struct TALER_DenominationHash *h_denom_pub,
const void *msg,
size_t msg_size,
enum TALER_ErrorCode *ec);
@@ -189,7 +197,7 @@ TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub,
* @param h_denom_pub hash of the public key to revoke
*/
void
-TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub);
+TEH_keys_denomination_revoke (const struct TALER_DenominationHash *h_denom_pub);
/**
@@ -366,7 +374,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
* #GNUNET_SYSERR on hard errors
*/
enum GNUNET_GenericReturnValue
-TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
+TEH_keys_load_fees (const struct TALER_DenominationHash *h_denom_pub,
struct TALER_DenominationPublicKey *denom_pub,
struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta);
@@ -378,7 +386,7 @@ TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
* @param[out] meta set to meta data about the key
* @return #GNUNET_OK on success
*/
-int
+enum GNUNET_GenericReturnValue
TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
struct TALER_EXCHANGEDB_SignkeyMetaData *meta);
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c
new file mode 100644
index 00000000..76d09481
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-check.c
@@ -0,0 +1,448 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_kyc-check.c
+ * @brief Handle request for generic KYC check.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_signatures.h"
+#include "taler_dbevents.h"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_kyc-wallet.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Reserve GET request that is long-polling.
+ */
+struct KycPoller
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct KycPoller *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct KycPoller *prev;
+
+ /**
+ * Connection we are handling.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Subscription for the database event we are
+ * waiting for.
+ */
+ struct GNUNET_DB_EventHandler *eh;
+
+ /**
+ * UUID being checked.
+ */
+ uint64_t payment_target_uuid;
+
+ /**
+ * Current KYC status.
+ */
+ struct TALER_EXCHANGEDB_KycStatus kyc;
+
+ /**
+ * Hash of the payto:// URI we are confirming to
+ * have finished the KYC for.
+ */
+ struct TALER_PaytoHash h_payto;
+
+ /**
+ * Hash of the payto:// URI that was given to us for auth.
+ */
+ struct TALER_PaytoHash auth_h_payto;
+
+ /**
+ * When will this request time out?
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * True if we are still suspended.
+ */
+ bool suspended;
+
+};
+
+
+/**
+ * Head of list of requests in long polling.
+ */
+static struct KycPoller *kyp_head;
+
+/**
+ * Tail of list of requests in long polling.
+ */
+static struct KycPoller *kyp_tail;
+
+
+void
+TEH_kyc_check_cleanup ()
+{
+ struct KycPoller *kyp;
+
+ while (NULL != (kyp = kyp_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (kyp_head,
+ kyp_tail,
+ kyp);
+ if (kyp->suspended)
+ {
+ kyp->suspended = false;
+ MHD_resume_connection (kyp->connection);
+ }
+ }
+}
+
+
+/**
+ * Function called once a connection is done to
+ * clean up the `struct ReservePoller` state.
+ *
+ * @param rc context to clean up for
+ */
+static void
+kyp_cleanup (struct TEH_RequestContext *rc)
+{
+ struct KycPoller *kyp = rc->rh_ctx;
+
+ GNUNET_assert (! kyp->suspended);
+ if (NULL != kyp->eh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Cancelling DB event listening\n");
+ TEH_plugin->event_listen_cancel (TEH_plugin->cls,
+ kyp->eh);
+ kyp->eh = NULL;
+ }
+ GNUNET_free (kyp);
+}
+
+
+/**
+ * Function implementing database transaction to check wallet's KYC status.
+ * Runs the transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct KycPoller *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+kyc_check (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct KycPoller *kyp = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->select_kyc_status (TEH_plugin->cls,
+ kyp->payment_target_uuid,
+ &kyp->h_payto,
+ &kyp->kyc);
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "inselect_wallet_status");
+ return qs;
+ }
+ return qs;
+}
+
+
+/**
+ * Function called on events received from Postgres.
+ * Wakes up long pollers.
+ *
+ * @param cls the `struct TEH_RequestContext *`
+ * @param extra additional event data provided
+ * @param extra_size number of bytes in @a extra
+ */
+static void
+db_event_cb (void *cls,
+ const void *extra,
+ size_t extra_size)
+{
+ struct TEH_RequestContext *rc = cls;
+ struct KycPoller *kyp = rc->rh_ctx;
+ struct GNUNET_AsyncScopeSave old_scope;
+
+ (void) extra;
+ (void) extra_size;
+ if (! kyp->suspended)
+ return; /* event triggered while main transaction
+ was still running, or got multiple wake-up events */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Received KYC update event\n");
+ kyp->suspended = false;
+ GNUNET_async_scope_enter (&rc->async_scope_id,
+ &old_scope);
+ TEH_check_invariants ();
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Resuming from long-polling on KYC status\n");
+ GNUNET_CONTAINER_DLL_remove (kyp_head,
+ kyp_tail,
+ kyp);
+ MHD_resume_connection (kyp->connection);
+ TALER_MHD_daemon_trigger ();
+ TEH_check_invariants ();
+ GNUNET_async_scope_restore (&old_scope);
+}
+
+
+MHD_RESULT
+TEH_handler_kyc_check (
+ struct TEH_RequestContext *rc,
+ const char *const args[])
+{
+ struct KycPoller *kyp = rc->rh_ctx;
+ MHD_RESULT res;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_TIME_Absolute now;
+
+ if (NULL == kyp)
+ {
+ kyp = GNUNET_new (struct KycPoller);
+ kyp->connection = rc->connection;
+ rc->rh_ctx = kyp;
+ rc->rh_cleaner = &kyp_cleanup;
+
+ {
+ unsigned long long payment_target_uuid;
+ char dummy;
+
+ if (1 !=
+ sscanf (args[0],
+ "%llu%c",
+ &payment_target_uuid,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "payment_target_uuid");
+ }
+ kyp->payment_target_uuid = (uint64_t) payment_target_uuid;
+ }
+ {
+ const char *ts;
+
+ ts = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "timeout_ms");
+ if (NULL != ts)
+ {
+ char dummy;
+ unsigned long long tms;
+
+ if (1 !=
+ sscanf (ts,
+ "%llu%c",
+ &tms,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "timeout_ms");
+ }
+ kyp->timeout = GNUNET_TIME_relative_to_absolute (
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ tms));
+ }
+ }
+ {
+ const char *hps;
+
+ hps = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "h_payto");
+ if (NULL == hps)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MISSING,
+ "h_payto");
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (hps,
+ strlen (hps),
+ &kyp->auth_h_payto,
+ sizeof (kyp->auth_h_payto)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "h_payto");
+ }
+ }
+ }
+
+ if (TEH_KYC_NONE == TEH_kyc_config.mode)
+ return TALER_MHD_reply_static (
+ rc->connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+
+ if ( (NULL == kyp->eh) &&
+ GNUNET_TIME_absolute_is_future (kyp->timeout) )
+ {
+ struct TALER_KycCompletedEventP rep = {
+ .header.size = htons (sizeof (rep)),
+ .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
+ .h_payto = kyp->auth_h_payto
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting DB event listening\n");
+ kyp->eh = TEH_plugin->event_listen (
+ TEH_plugin->cls,
+ GNUNET_TIME_absolute_get_remaining (kyp->timeout),
+ &rep.header,
+ &db_event_cb,
+ rc);
+ }
+
+ now = GNUNET_TIME_absolute_get ();
+ (void) GNUNET_TIME_round_abs (&now);
+ ret = TEH_DB_run_transaction (rc->connection,
+ "kyc check",
+ &res,
+ &kyc_check,
+ kyp);
+ if (GNUNET_SYSERR == ret)
+ return res;
+ if (0 !=
+ GNUNET_memcmp (&kyp->h_payto,
+ &kyp->auth_h_payto))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_UNAUTHORIZED,
+ TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
+ "h_payto");
+ }
+
+ /* long polling? */
+ if ( (! kyp->kyc.ok) &&
+ GNUNET_TIME_absolute_is_future (kyp->timeout))
+ {
+ GNUNET_assert (NULL != kyp->eh);
+ kyp->suspended = true;
+ GNUNET_CONTAINER_DLL_insert (kyp_head,
+ kyp_tail,
+ kyp);
+ MHD_suspend_connection (kyp->connection);
+ return MHD_YES;
+ }
+
+ /* KYC failed? */
+ if (! kyp->kyc.ok)
+ {
+ char *url;
+ char *redirect_uri;
+ char *redirect_uri_encoded;
+
+ GNUNET_assert (TEH_KYC_OAUTH2 == TEH_kyc_config.mode);
+ GNUNET_asprintf (&redirect_uri,
+ "%s/kyc-proof/%llu",
+ TEH_base_url,
+ (unsigned long long) kyp->payment_target_uuid);
+ redirect_uri_encoded = TALER_urlencode (redirect_uri);
+ GNUNET_free (redirect_uri);
+ GNUNET_asprintf (&url,
+ "%s/login?client_id=%s&redirect_uri=%s",
+ TEH_kyc_config.details.oauth2.url,
+ TEH_kyc_config.details.oauth2.client_id,
+ redirect_uri_encoded);
+ GNUNET_free (redirect_uri_encoded);
+
+ res = TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_ACCEPTED,
+ GNUNET_JSON_pack_string ("kyc_url",
+ url));
+ GNUNET_free (url);
+ return res;
+ }
+
+ /* KYC succeeded! */
+ {
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+ struct TALER_ExchangeAccountSetupSuccessPS as = {
+ .purpose.purpose = htonl (
+ TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS),
+ .purpose.size = htonl (sizeof (as)),
+ .h_payto = kyp->h_payto,
+ .timestamp = GNUNET_TIME_absolute_hton (now)
+ };
+ enum TALER_ErrorCode ec;
+
+ if (TALER_EC_NONE !=
+ (ec = TEH_keys_exchange_sign (&as,
+ &pub,
+ &sig)))
+ {
+ return TALER_MHD_reply_with_ec (rc->connection,
+ ec,
+ NULL);
+ }
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_data_auto ("exchange_sig",
+ &sig),
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
+ &pub),
+ GNUNET_JSON_pack_time_abs ("now",
+ now));
+ }
+}
+
+
+/* end of taler-exchange-httpd_kyc-check.c */
diff --git a/src/exchange/taler-exchange-httpd_kyc-check.h b/src/exchange/taler-exchange-httpd_kyc-check.h
new file mode 100644
index 00000000..52fe5f16
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-check.h
@@ -0,0 +1,49 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_kyc-check.h
+ * @brief Handle /kyc-check requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_KYC_CHECK_H
+#define TALER_EXCHANGE_HTTPD_KYC_CHECK_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a "/kyc-check" request. Checks the KYC
+ * status of the given account and returns it.
+ *
+ * @param connection request to handle
+ * @param args one argument with the payment_target_uuid
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_kyc_check (
+ struct TEH_RequestContext *rc,
+ const char *const args[]);
+
+
+/**
+ * Clean up long-polling KYC requests during shutdown.
+ */
+void
+TEH_kyc_check_cleanup (void);
+
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c
new file mode 100644
index 00000000..6bd98abf
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-proof.c
@@ -0,0 +1,714 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_kyc-proof.c
+ * @brief Handle request for proof for KYC check.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_kyc-proof.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Context for the proof.
+ */
+struct KycProofContext
+{
+
+ /**
+ * Kept in a DLL while suspended.
+ */
+ struct KycProofContext *next;
+
+ /**
+ * Kept in a DLL while suspended.
+ */
+ struct KycProofContext *prev;
+
+ /**
+ * Details about the connection we are processing.
+ */
+ struct TEH_RequestContext *rc;
+
+ /**
+ * Handle for the OAuth 2.0 CURL request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * OAuth 2.0 authorization code.
+ */
+ const char *authorization_code;
+
+ /**
+ * OAuth 2.0 token URL we are using for the
+ * request.
+ */
+ char *token_url;
+
+ /**
+ * Body of the POST request.
+ */
+ char *post_body;
+
+ /**
+ * User ID extracted from the OAuth 2.0 service, or NULL.
+ */
+ char *id;
+
+ /**
+ * Payment target this is about.
+ */
+ unsigned long long payment_target_uuid;
+
+ /**
+ * HTTP response to return.
+ */
+ struct MHD_Response *response;
+
+ /**
+ * HTTP response code to return.
+ */
+ unsigned int response_code;
+
+ /**
+ * #GNUNET_YES if we are suspended,
+ * #GNUNET_NO if not.
+ * #GNUNET_SYSERR if we had some error.
+ */
+ enum GNUNET_GenericReturnValue suspended;
+
+};
+
+
+/**
+ * Contexts are kept in a DLL while suspended.
+ */
+static struct KycProofContext *kpc_head;
+
+/**
+ * Contexts are kept in a DLL while suspended.
+ */
+static struct KycProofContext *kpc_tail;
+
+
+/**
+ * Resume processing the @a kpc request.
+ *
+ * @param kpc request to resume
+ */
+static void
+kpc_resume (struct KycProofContext *kpc)
+{
+ GNUNET_assert (GNUNET_YES == kpc->suspended);
+ kpc->suspended = GNUNET_NO;
+ GNUNET_CONTAINER_DLL_remove (kpc_head,
+ kpc_tail,
+ kpc);
+ MHD_resume_connection (kpc->rc->connection);
+ TALER_MHD_daemon_trigger ();
+}
+
+
+void
+TEH_kyc_proof_cleanup (void)
+{
+ struct KycProofContext *kpc;
+
+ while (NULL != (kpc = kpc_head))
+ {
+ if (NULL != kpc->job)
+ {
+ GNUNET_CURL_job_cancel (kpc->job);
+ kpc->job = NULL;
+ }
+ kpc_resume (kpc);
+ }
+}
+
+
+/**
+ * Function implementing database transaction to check proof's KYC status.
+ * Runs the transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct KycProofContext *`
+ * @param connection MHD proof which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+persist_kyc_ok (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct KycProofContext *kpc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->set_kyc_ok (TEH_plugin->cls,
+ kpc->payment_target_uuid,
+ kpc->id);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "set_kyc_ok");
+ }
+ return qs;
+}
+
+
+/**
+ * The request for @a kpc failed. We may have gotten a useful error
+ * message in @a j. Generate a failure response.
+ *
+ * @param[in,out] kpc request that failed
+ * @param j reply from the server (or NULL)
+ */
+static void
+handle_error (struct KycProofContext *kpc,
+ const json_t *j)
+{
+ const char *msg;
+ const char *desc;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("error",
+ &msg),
+ GNUNET_JSON_spec_string ("error_description",
+ &desc),
+ GNUNET_JSON_spec_end ()
+ };
+
+ {
+ enum GNUNET_GenericReturnValue res;
+ const char *emsg;
+ unsigned int line;
+
+ res = GNUNET_JSON_parse (j,
+ spec,
+ &emsg,
+ &line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ "Unexpected response from KYC gateway");
+ kpc->response_code
+ = MHD_HTTP_BAD_GATEWAY;
+ return;
+ }
+ }
+ /* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED,
+ we MAY want to in the future look at the requested content type
+ and possibly respond in JSON if indicated. */
+ {
+ char *reply;
+
+ GNUNET_asprintf (&reply,
+ "<html><head><title>%s</title></head><body><h1>%s</h1><p>%s</p></body></html>",
+ msg,
+ msg,
+ desc);
+ kpc->response
+ = MHD_create_response_from_buffer (strlen (reply),
+ reply,
+ MHD_RESPMEM_MUST_COPY);
+ GNUNET_assert (NULL != kpc->response);
+ GNUNET_free (reply);
+ }
+ kpc->response_code = MHD_HTTP_FORBIDDEN;
+}
+
+
+/**
+ * The request for @a kpc succeeded (presumably).
+ * Parse the user ID and store it in @a kpc (if possible).
+ *
+ * @param[in,out] kpc request that succeeded
+ * @param j reply from the server
+ */
+static void
+parse_success_reply (struct KycProofContext *kpc,
+ const json_t *j)
+{
+ const char *state;
+ json_t *data;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &state),
+ GNUNET_JSON_spec_json ("data",
+ &data),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue res;
+ const char *emsg;
+ unsigned int line;
+
+ res = GNUNET_JSON_parse (j,
+ spec,
+ &emsg,
+ &line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ "Unexpected response from KYC gateway");
+ kpc->response_code
+ = MHD_HTTP_BAD_GATEWAY;
+ return;
+ }
+ if (0 != strcasecmp (state,
+ "success"))
+ {
+ GNUNET_break_op (0);
+ handle_error (kpc,
+ j);
+ return;
+ }
+ {
+ const char *id;
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_string ("id",
+ &id),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = GNUNET_JSON_parse (data,
+ ispec,
+ &emsg,
+ &line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ "Unexpected response from KYC gateway");
+ kpc->response_code
+ = MHD_HTTP_BAD_GATEWAY;
+ return;
+ }
+ kpc->id = GNUNET_strdup (id);
+ }
+}
+
+
+/**
+ * After we are done with the CURL interaction we
+ * need to update our database state with the information
+ * retrieved.
+ *
+ * @param cls our `struct KycProofContext`
+ * @param response_code HTTP response code from server, 0 on hard error
+ * @param response in JSON, NULL if response was not in JSON format
+ */
+static void
+handle_curl_fetch_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct KycProofContext *kpc = cls;
+ const json_t *j = response;
+
+ kpc->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ parse_success_reply (kpc,
+ j);
+ break;
+ default:
+ handle_error (kpc,
+ j);
+ break;
+ }
+ kpc_resume (kpc);
+}
+
+
+/**
+ * After we are done with the CURL interaction we
+ * need to fetch the user's account details.
+ *
+ * @param cls our `struct KycProofContext`
+ * @param response_code HTTP response code from server, 0 on hard error
+ * @param response in JSON, NULL if response was not in JSON format
+ */
+static void
+handle_curl_login_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct KycProofContext *kpc = cls;
+ const json_t *j = response;
+
+ kpc->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const char *access_token;
+ const char *token_type;
+ uint64_t expires_in_s;
+ const char *refresh_token;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("access_token",
+ &access_token),
+ GNUNET_JSON_spec_string ("token_type",
+ &token_type),
+ GNUNET_JSON_spec_uint64 ("expires_in",
+ &expires_in_s),
+ GNUNET_JSON_spec_string ("refresh_token",
+ &refresh_token),
+ GNUNET_JSON_spec_end ()
+ };
+ CURL *eh;
+
+ {
+ enum GNUNET_GenericReturnValue res;
+ const char *emsg;
+ unsigned int line;
+
+ res = GNUNET_JSON_parse (j,
+ spec,
+ &emsg,
+ &line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ "Unexpected response from KYC gateway");
+ kpc->response_code
+ = MHD_HTTP_BAD_GATEWAY;
+ break;
+ }
+ }
+ if (0 != strcasecmp (token_type,
+ "bearer"))
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ "Unexpected token type in response from KYC gateway");
+ kpc->response_code
+ = MHD_HTTP_BAD_GATEWAY;
+ break;
+ }
+
+ /* We guard against a few characters that could
+ conceivably be abused to mess with the HTTP header */
+ if ( (NULL != strchr (access_token,
+ '\n')) ||
+ (NULL != strchr (access_token,
+ '\r')) ||
+ (NULL != strchr (access_token,
+ ' ')) ||
+ (NULL != strchr (access_token,
+ ';')) )
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
+ "Illegal character in access token");
+ kpc->response_code
+ = MHD_HTTP_BAD_GATEWAY;
+ break;
+ }
+
+ eh = curl_easy_init ();
+ if (NULL == eh)
+ {
+ GNUNET_break_op (0);
+ kpc->response
+ = TALER_MHD_make_error (
+ TALER_EC_GENERIC_ALLOCATION_FAILURE,
+ "curl_easy_init");
+ kpc->response_code
+ = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ TEH_kyc_config.details.oauth2.info_url));
+ {
+ char *hdr;
+ struct curl_slist *slist;
+
+ GNUNET_asprintf (&hdr,
+ "%s: Bearer %s",
+ MHD_HTTP_HEADER_AUTHORIZATION,
+ access_token);
+ slist = curl_slist_append (NULL,
+ hdr);
+ kpc->job = GNUNET_CURL_job_add2 (TEH_curl_ctx,
+ eh,
+ slist,
+ &handle_curl_fetch_finished,
+ kpc);
+ curl_slist_free_all (slist);
+ GNUNET_free (hdr);
+ }
+ return;
+ }
+ default:
+ handle_error (kpc,
+ j);
+ break;
+ }
+ kpc_resume (kpc);
+}
+
+
+/**
+ * Function called to clean up a context.
+ *
+ * @param rc request context
+ */
+static void
+clean_kpc (struct TEH_RequestContext *rc)
+{
+ struct KycProofContext *kpc = rc->rh_ctx;
+
+ if (NULL != kpc->job)
+ {
+ GNUNET_CURL_job_cancel (kpc->job);
+ kpc->job = NULL;
+ }
+ if (NULL != kpc->response)
+ {
+ MHD_destroy_response (kpc->response);
+ kpc->response = NULL;
+ }
+ GNUNET_free (kpc->post_body);
+ GNUNET_free (kpc->token_url);
+ GNUNET_free (kpc->id);
+ GNUNET_free (kpc);
+}
+
+
+MHD_RESULT
+TEH_handler_kyc_proof (
+ struct TEH_RequestContext *rc,
+ const char *const args[])
+{
+ struct KycProofContext *kpc = rc->rh_ctx;
+
+ if (NULL == kpc)
+ { /* first time */
+ char dummy;
+
+ kpc = GNUNET_new (struct KycProofContext);
+ kpc->rc = rc;
+ rc->rh_ctx = kpc;
+ rc->rh_cleaner = &clean_kpc;
+
+ if (1 !=
+ sscanf (args[0],
+ "%llu%c",
+ &kpc->payment_target_uuid,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "payment_target_uuid");
+ }
+ kpc->authorization_code
+ = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "code");
+ if (NULL == kpc->authorization_code)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "code");
+ }
+ if (TEH_KYC_NONE == TEH_kyc_config.mode)
+ return TALER_MHD_reply_static (
+ rc->connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+
+ {
+ CURL *eh;
+
+ eh = curl_easy_init ();
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_ALLOCATION_FAILURE,
+ "curl_easy_init");
+ }
+ GNUNET_asprintf (&kpc->token_url,
+ "%stoken",
+ TEH_kyc_config.details.oauth2.url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ kpc->token_url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POST,
+ 1));
+ {
+ char *client_id;
+ char *redirect_uri;
+ char *client_secret;
+ char *authorization_code;
+
+ client_id = curl_easy_escape (eh,
+ TEH_kyc_config.details.oauth2.client_id,
+ 0);
+ GNUNET_assert (NULL != client_id);
+ {
+ char *request_uri;
+
+ GNUNET_asprintf (&request_uri,
+ "%slogin?client_id=%s",
+ TEH_kyc_config.details.oauth2.url,
+ TEH_kyc_config.details.oauth2.client_id);
+ redirect_uri = curl_easy_escape (eh,
+ request_uri,
+ 0);
+ GNUNET_free (request_uri);
+ }
+ GNUNET_assert (NULL != redirect_uri);
+ client_secret = curl_easy_escape (eh,
+ TEH_kyc_config.details.oauth2.
+ client_secret,
+ 0);
+ GNUNET_assert (NULL != client_secret);
+ authorization_code = curl_easy_escape (eh,
+ kpc->authorization_code,
+ 0);
+ GNUNET_assert (NULL != authorization_code);
+ GNUNET_asprintf (&kpc->post_body,
+ "client_id=%s&redirect_uri=%s&client_secret=%s&code=%s&grant_type=authorization_code",
+ client_id,
+ redirect_uri,
+ client_secret,
+ authorization_code);
+ curl_free (authorization_code);
+ curl_free (client_secret);
+ curl_free (redirect_uri);
+ curl_free (client_id);
+ }
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDS,
+ kpc->post_body));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_FOLLOWLOCATION,
+ 1L));
+ /* limit MAXREDIRS to 5 as a simple security measure against
+ a potential infinite loop caused by a malicious target */
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_MAXREDIRS,
+ 5L));
+
+ kpc->job = GNUNET_CURL_job_add (TEH_curl_ctx,
+ eh,
+ &handle_curl_login_finished,
+ kpc);
+ kpc->suspended = GNUNET_YES;
+ GNUNET_CONTAINER_DLL_insert (kpc_head,
+ kpc_tail,
+ kpc);
+ MHD_suspend_connection (rc->connection);
+ return MHD_YES;
+ }
+ }
+
+ if (NULL != kpc->response)
+ {
+ /* handle _failed_ resumed cases */
+ return MHD_queue_response (rc->connection,
+ kpc->response_code,
+ kpc->response);
+ }
+
+ /* _successfully_ resumed case */
+ {
+ MHD_RESULT res;
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = TEH_DB_run_transaction (kpc->rc->connection,
+ "check proof kyc",
+ &res,
+ &persist_kyc_ok,
+ kpc);
+ if (GNUNET_SYSERR == ret)
+ return res;
+ }
+
+ {
+ struct MHD_Response *response;
+ MHD_RESULT res;
+
+ response = MHD_create_response_from_buffer (0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (
+ response,
+ MHD_HTTP_HEADER_LOCATION,
+ TEH_kyc_config.details.oauth2.post_kyc_redirect_url));
+ res = MHD_queue_response (rc->connection,
+ MHD_HTTP_SEE_OTHER,
+ response);
+ MHD_destroy_response (response);
+ return res;
+ }
+}
+
+
+/* end of taler-exchange-httpd_kyc-proof.c */
diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.h b/src/exchange/taler-exchange-httpd_kyc-proof.h
new file mode 100644
index 00000000..c075b242
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-proof.h
@@ -0,0 +1,49 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_kyc-proof.h
+ * @brief Handle /kyc-proof requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_KYC_PROOF_H
+#define TALER_EXCHANGE_HTTPD_KYC_PROOF_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Shutdown kyc-proof subsystem. Resumes all suspended long-polling clients
+ * and cleans up data structures.
+ */
+void
+TEH_kyc_proof_cleanup (void);
+
+
+/**
+ * Handle a "/kyc-proof" request.
+ *
+ * @param rc request to handle
+ * @param args one argument with the payment_target_uuid
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_kyc_proof (
+ struct TEH_RequestContext *rc,
+ const char *const args[]);
+
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_kyc-wallet.c b/src/exchange/taler-exchange-httpd_kyc-wallet.c
new file mode 100644
index 00000000..3db174bf
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-wallet.c
@@ -0,0 +1,156 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_kyc-wallet.c
+ * @brief Handle request for wallet for KYC check.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_kyc-wallet.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Context for the request.
+ */
+struct KycRequestContext
+{
+ /**
+ * Public key of the reserve/wallet this is about.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Current KYC status.
+ */
+ struct TALER_EXCHANGEDB_KycStatus kyc;
+};
+
+
+/**
+ * Function implementing database transaction to check wallet's KYC status.
+ * Runs the transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct KycRequestContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+wallet_kyc_check (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct KycRequestContext *krc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->inselect_wallet_kyc_status (TEH_plugin->cls,
+ &krc->reserve_pub,
+ &krc->kyc);
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "inselect_wallet_status");
+ return qs;
+ }
+ return qs;
+}
+
+
+MHD_RESULT
+TEH_handler_kyc_wallet (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[])
+{
+ struct TALER_ReserveSignatureP reserve_sig;
+ struct KycRequestContext krc;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+ &reserve_sig),
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &krc.reserve_pub),
+ GNUNET_JSON_spec_end ()
+ };
+ MHD_RESULT res;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
+ .size = htonl (sizeof (purpose)),
+ .purpose = htonl (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP)
+ };
+
+ (void) args;
+ ret = TALER_MHD_parse_json_data (rc->connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == ret)
+ return MHD_NO; /* hard failure */
+ if (GNUNET_NO == ret)
+ return MHD_YES; /* failure */
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify_ (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP,
+ &purpose,
+ &reserve_sig.eddsa_signature,
+ &krc.reserve_pub.eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID,
+ NULL);
+ }
+ if (TEH_KYC_NONE == TEH_kyc_config.mode)
+ return TALER_MHD_reply_static (
+ rc->connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ ret = TEH_DB_run_transaction (rc->connection,
+ "check wallet kyc",
+ &res,
+ &wallet_kyc_check,
+ &krc);
+ if (GNUNET_SYSERR == ret)
+ return res;
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_uint64 ("payment_target_uuid",
+ krc.kyc.payment_target_uuid));
+}
+
+
+/* end of taler-exchange-httpd_kyc-wallet.c */
diff --git a/src/exchange/taler-exchange-httpd_kyc-wallet.h b/src/exchange/taler-exchange-httpd_kyc-wallet.h
new file mode 100644
index 00000000..bd8ae1b0
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_kyc-wallet.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_kyc-wallet.h
+ * @brief Handle /kyc-wallet requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_KYC_WALLET_H
+#define TALER_EXCHANGE_HTTPD_KYC_WALLET_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a "/kyc-wallet" request. Parses the "reserve_pub" EdDSA key of the
+ * reserve and the signature "reserve_sig" which affirms the operation. If OK,
+ * a KYC record is created (if missing) and the KYC status returned.
+ *
+ * @param rc request to handle
+ * @param root uploaded JSON data
+ * @param args empty array
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_kyc_wallet (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[]);
+
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_link.c b/src/exchange/taler-exchange-httpd_link.c
index 75d32e56..3393e068 100644
--- a/src/exchange/taler-exchange-httpd_link.c
+++ b/src/exchange/taler-exchange-httpd_link.c
@@ -82,10 +82,10 @@ handle_link_data (void *cls,
json_t *obj;
obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_rsa_public_key ("denom_pub",
- pos->denom_pub.rsa_public_key),
- GNUNET_JSON_pack_rsa_signature ("ev_sig",
- pos->ev_sig.rsa_signature),
+ TALER_JSON_pack_denom_pub ("denom_pub",
+ &pos->denom_pub),
+ TALER_JSON_pack_blinded_denom_sig ("ev_sig",
+ &pos->ev_sig),
GNUNET_JSON_pack_data_auto ("link_sig",
&pos->orig_coin_link_sig));
if ( (NULL == obj) ||
diff --git a/src/exchange/taler-exchange-httpd_management.h b/src/exchange/taler-exchange-httpd_management.h
index 3f58083e..d46aad9e 100644
--- a/src/exchange/taler-exchange-httpd_management.h
+++ b/src/exchange/taler-exchange-httpd_management.h
@@ -64,7 +64,7 @@ TEH_handler_management_auditors_AP_disable (
MHD_RESULT
TEH_handler_management_denominations_HDP_revoke (
struct MHD_Connection *connection,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHash *h_denom_pub,
const json_t *root);
diff --git a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
index 6519404d..a8acf2f7 100644
--- a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
+++ b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
@@ -34,7 +34,7 @@
MHD_RESULT
TEH_handler_management_denominations_HDP_revoke (
struct MHD_Connection *connection,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHash *h_denom_pub,
const json_t *root)
{
struct TALER_MasterSignatureP master_sig;
diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c b/src/exchange/taler-exchange-httpd_management_post_keys.c
index 311fff78..86b17cb3 100644
--- a/src/exchange/taler-exchange-httpd_management_post_keys.c
+++ b/src/exchange/taler-exchange-httpd_management_post_keys.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020, 2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -40,7 +40,7 @@ struct DenomSig
/**
* Hash of a denomination public key.
*/
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHash h_denom_pub;
/**
* Master signature for the @e h_denom_pub.
@@ -121,15 +121,19 @@ add_keys (void *cls,
/* activate all denomination keys */
for (unsigned int i = 0; i<akc->nd_sigs; i++)
{
+ struct DenomSig *d = &akc->d_sigs[i];
enum GNUNET_DB_QueryStatus qs;
bool is_active = false;
struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
struct TALER_DenominationPublicKey denom_pub;
/* For idempotency, check if the key is already active */
+ memset (&denom_pub,
+ 0,
+ sizeof (denom_pub));
qs = TEH_plugin->lookup_denomination_key (
TEH_plugin->cls,
- &akc->d_sigs[i].h_denom_pub,
+ &d->h_denom_pub,
&meta);
if (qs < 0)
{
@@ -146,7 +150,7 @@ add_keys (void *cls,
{
enum GNUNET_GenericReturnValue rv;
- rv = TEH_keys_load_fees (&akc->d_sigs[i].h_denom_pub,
+ rv = TEH_keys_load_fees (&d->h_denom_pub,
&denom_pub,
&meta);
switch (rv)
@@ -156,14 +160,14 @@ add_keys (void *cls,
connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
- GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
+ GNUNET_h2s (&d->h_denom_pub.hash));
return GNUNET_DB_STATUS_HARD_ERROR;
case GNUNET_NO:
*mhd_ret = TALER_MHD_reply_with_error (
connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
- GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
+ GNUNET_h2s (&d->h_denom_pub.hash));
return GNUNET_DB_STATUS_HARD_ERROR;
case GNUNET_OK:
break;
@@ -175,40 +179,45 @@ add_keys (void *cls,
}
/* check signature is valid */
+ if (GNUNET_OK !=
+ TALER_exchange_offline_denom_validity_verify (
+ &d->h_denom_pub,
+ meta.start,
+ meta.expire_withdraw,
+ meta.expire_deposit,
+ meta.expire_legal,
+ &meta.value,
+ &meta.fee_withdraw,
+ &meta.fee_deposit,
+ &meta.fee_refresh,
+ &meta.fee_refund,
+ &TEH_master_public_key,
+ &d->master_sig))
{
- if (GNUNET_OK !=
- TALER_exchange_offline_denom_validity_verify (
- &akc->d_sigs[i].h_denom_pub,
- meta.start,
- meta.expire_withdraw,
- meta.expire_deposit,
- meta.expire_legal,
- &meta.value,
- &meta.fee_withdraw,
- &meta.fee_deposit,
- &meta.fee_refresh,
- &meta.fee_refund,
- &TEH_master_public_key,
- &akc->d_sigs[i].master_sig))
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID,
- GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
+ GNUNET_break_op (0);
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID,
+ GNUNET_h2s (&d->h_denom_pub.hash));
+ if (! is_active)
+ TALER_denom_pub_free (&denom_pub);
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
if (is_active)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Denomination key %s already active, skipping\n",
+ GNUNET_h2s (&d->h_denom_pub.hash));
continue; /* skip, already known */
+ }
qs = TEH_plugin->add_denomination_key (
TEH_plugin->cls,
- &akc->d_sigs[i].h_denom_pub,
+ &d->h_denom_pub,
&denom_pub,
&meta,
- &akc->d_sigs[i].master_sig);
- GNUNET_CRYPTO_rsa_public_key_free (denom_pub.rsa_public_key);
+ &d->master_sig);
+ TALER_denom_pub_free (&denom_pub);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -222,20 +231,20 @@ add_keys (void *cls,
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Added offline signature for denomination `%s'\n",
- GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
+ GNUNET_h2s (&d->h_denom_pub.hash));
GNUNET_assert (0 != qs);
}
-
for (unsigned int i = 0; i<akc->ns_sigs; i++)
{
+ struct SigningSig *s = &akc->s_sigs[i];
enum GNUNET_DB_QueryStatus qs;
bool is_active = false;
struct TALER_EXCHANGEDB_SignkeyMetaData meta;
qs = TEH_plugin->lookup_signing_key (
TEH_plugin->cls,
- &akc->s_sigs[i].exchange_pub,
+ &s->exchange_pub,
&meta);
if (qs < 0)
{
@@ -251,7 +260,7 @@ add_keys (void *cls,
if (0 == qs)
{
if (GNUNET_OK !=
- TEH_keys_get_timing (&akc->s_sigs[i].exchange_pub,
+ TEH_keys_get_timing (&s->exchange_pub,
&meta))
{
/* For idempotency, check if the key is already active */
@@ -259,7 +268,7 @@ add_keys (void *cls,
connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN,
- TALER_B2S (&akc->s_sigs[i].exchange_pub));
+ TALER_B2S (&s->exchange_pub));
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
@@ -269,32 +278,35 @@ add_keys (void *cls,
}
/* check signature is valid */
+ if (GNUNET_OK !=
+ TALER_exchange_offline_signkey_validity_verify (
+ &s->exchange_pub,
+ meta.start,
+ meta.expire_sign,
+ meta.expire_legal,
+ &TEH_master_public_key,
+ &s->master_sig))
{
- if (GNUNET_OK !=
- TALER_exchange_offline_signkey_validity_verify (
- &akc->s_sigs[i].exchange_pub,
- meta.start,
- meta.expire_sign,
- meta.expire_legal,
- &TEH_master_public_key,
- &akc->s_sigs[i].master_sig))
- {
- GNUNET_break_op (0);
- *mhd_ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID,
- GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
+ GNUNET_break_op (0);
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID,
+ TALER_B2S (&s->exchange_pub));
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
if (is_active)
- continue; /* skip, already known */
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Signing key %s already active, skipping\n",
+ TALER_B2S (&s->exchange_pub));
+ continue; /* skip, already known */
+ }
qs = TEH_plugin->activate_signing_key (
TEH_plugin->cls,
- &akc->s_sigs[i].exchange_pub,
+ &s->exchange_pub,
&meta,
- &akc->s_sigs[i].master_sig);
+ &s->master_sig);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -308,7 +320,7 @@ add_keys (void *cls,
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Added offline signature for signing key `%s'\n",
- TALER_B2S (&akc->s_sigs[i].exchange_pub));
+ TALER_B2S (&s->exchange_pub));
GNUNET_assert (0 != qs);
}
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c
index af0a0a63..c33473b4 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -494,7 +494,7 @@ check_for_denomination_key (struct MHD_Connection *connection,
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit))
{
/* We are past deposit expiration time, but maybe this is a zombie? */
- struct GNUNET_HashCode denom_hash;
+ struct TALER_DenominationHash denom_hash;
enum GNUNET_DB_QueryStatus qs;
/* Check that the coin is dirty (we have seen it before), as we will
@@ -590,8 +590,8 @@ TEH_handler_melt (struct MHD_Connection *connection,
enum GNUNET_GenericReturnValue ret;
MHD_RESULT res;
struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_denomination_signature ("denom_sig",
- &rmc.refresh_session.coin.denom_sig),
+ TALER_JSON_spec_denom_sig ("denom_sig",
+ &rmc.refresh_session.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&rmc.refresh_session.coin.denom_pub_hash),
GNUNET_JSON_spec_fixed_auto ("confirm_sig",
diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c
index 137034a4..b5074ce3 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -41,7 +41,7 @@ struct RecoupContext
/**
* Hash of the blinded coin.
*/
- struct GNUNET_HashCode h_blind;
+ struct TALER_BlindedCoinHash h_blind;
/**
* Full value of the coin.
@@ -56,7 +56,7 @@ struct RecoupContext
/**
* Key used to blind the coin.
*/
- const struct TALER_DenominationBlindingKeyP *coin_bks;
+ const union TALER_DenominationBlindingKeyP *coin_bks;
/**
* Signature of the coin requesting recoup.
@@ -179,7 +179,7 @@ recoup_transaction (void *cls,
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Recoup requested for unknown envelope %s\n",
- GNUNET_h2s (&pc->h_blind));
+ GNUNET_h2s (&pc->h_blind.hash));
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_RECOUP_WITHDRAW_NOT_FOUND,
@@ -342,18 +342,16 @@ recoup_transaction (void *cls,
* @return MHD result code
*/
static MHD_RESULT
-verify_and_execute_recoup (struct MHD_Connection *connection,
- const struct TALER_CoinPublicInfo *coin,
- const struct
- TALER_DenominationBlindingKeyP *coin_bks,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- int refreshed)
+verify_and_execute_recoup (
+ struct MHD_Connection *connection,
+ const struct TALER_CoinPublicInfo *coin,
+ const union TALER_DenominationBlindingKeyP *coin_bks,
+ const struct TALER_CoinSpendSignatureP *coin_sig,
+ int refreshed)
{
struct RecoupContext pc;
const struct TEH_DenominationKey *dk;
- struct GNUNET_HashCode c_hash;
- void *coin_ev;
- size_t coin_ev_size;
+ struct TALER_CoinPubHash c_hash;
MHD_RESULT mret;
/* check denomination exists and is in recoup mode */
@@ -423,7 +421,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
{
struct TALER_RecoupRequestPS pr = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
- .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
+ .purpose.size = htonl (sizeof (pr)),
.coin_pub = coin->coin_pub,
.h_denom_pub = coin->denom_pub_hash,
.coin_blind = *coin_bks
@@ -442,26 +440,31 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
NULL);
}
}
- GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub,
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
- &c_hash);
- if (GNUNET_YES !=
- TALER_rsa_blind (&c_hash,
- &coin_bks->bks,
- dk->denom_pub.rsa_public_key,
- &coin_ev,
- &coin_ev_size))
+
{
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
- NULL);
+ void *coin_ev;
+ size_t coin_ev_size;
+
+ if (GNUNET_OK !=
+ TALER_denom_blind (&dk->denom_pub,
+ coin_bks,
+ NULL, /* FIXME-Oec: TALER_AgeHash * */
+ &coin->coin_pub,
+ &c_hash,
+ &coin_ev,
+ &coin_ev_size))
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
+ NULL);
+ }
+ TALER_coin_ev_hash (coin_ev,
+ coin_ev_size,
+ &pc.h_blind);
+ GNUNET_free (coin_ev);
}
- GNUNET_CRYPTO_hash (coin_ev,
- coin_ev_size,
- &pc.h_blind);
- GNUNET_free (coin_ev);
/* Perform actual recoup transaction */
pc.coin_sig = coin_sig;
@@ -516,14 +519,14 @@ TEH_handler_recoup (struct MHD_Connection *connection,
{
enum GNUNET_GenericReturnValue ret;
struct TALER_CoinPublicInfo coin;
- struct TALER_DenominationBlindingKeyP coin_bks;
+ union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_CoinSpendSignatureP coin_sig;
int refreshed = GNUNET_NO;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&coin.denom_pub_hash),
- TALER_JSON_spec_denomination_signature ("denom_sig",
- &coin.denom_sig),
+ TALER_JSON_spec_denom_sig ("denom_sig",
+ &coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("coin_blind_key_secret",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 4ca6bd15..4ec70313 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -55,7 +55,8 @@
static MHD_RESULT
reply_refreshes_reveal_success (struct MHD_Connection *connection,
unsigned int num_freshcoins,
- const struct TALER_DenominationSignature *sigs)
+ const struct
+ TALER_BlindedDenominationSignature *sigs)
{
json_t *list;
@@ -68,8 +69,8 @@ reply_refreshes_reveal_success (struct MHD_Connection *connection,
json_t *obj;
obj = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_rsa_signature ("ev_sig",
- sigs[freshcoin_index].rsa_signature));
+ TALER_JSON_pack_blinded_denom_sig ("ev_sig",
+ &sigs[freshcoin_index]));
GNUNET_assert (0 ==
json_array_append_new (list,
obj));
@@ -123,7 +124,7 @@ struct RevealContext
/**
* Envelopes with the signatures to be returned. Initially NULL.
*/
- struct TALER_DenominationSignature *ev_sigs;
+ struct TALER_BlindedDenominationSignature *ev_sigs;
/**
* Size of the @e dks, @e rcds and @e ev_sigs arrays (if non-NULL).
@@ -187,10 +188,10 @@ check_exists_cb (void *cls,
if (NULL == rctx->ev_sigs)
{
rctx->ev_sigs = GNUNET_new_array (num_freshcoins,
- struct TALER_DenominationSignature);
+ struct TALER_BlindedDenominationSignature);
for (unsigned int i = 0; i<num_freshcoins; i++)
- rctx->ev_sigs[i].rsa_signature
- = GNUNET_CRYPTO_rsa_signature_dup (rrcs[i].coin_sig.rsa_signature);
+ TALER_blinded_denom_sig_deep_copy (&rctx->ev_sigs[i],
+ &rrcs[i].coin_sig);
}
}
@@ -334,7 +335,7 @@ refreshes_reveal_transaction (void *cls,
struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
struct TALER_PlanchetSecretsP ps;
struct TALER_PlanchetDetail pd;
- struct GNUNET_HashCode c_hash;
+ struct TALER_CoinPubHash c_hash;
rcd->dk = &rctx->dks[j]->denom_pub;
TALER_planchet_setup_refresh (&ts,
@@ -500,7 +501,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
/* We know num_fresh_coins is bounded by #MAX_FRESH_COINS, so this is safe */
const struct TEH_DenominationKey *dks[num_fresh_coins];
- struct GNUNET_HashCode dk_h[num_fresh_coins];
+ struct TALER_DenominationHash dk_h[num_fresh_coins];
struct TALER_RefreshCoinData rcds[num_fresh_coins];
struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
struct TALER_EXCHANGEDB_Melt melt;
@@ -683,10 +684,10 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
/* sign _early_ (optimistic!) to keep out of transaction scope! */
rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins,
- struct TALER_DenominationSignature);
+ struct TALER_BlindedDenominationSignature);
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
{
- enum TALER_ErrorCode ec;
+ enum TALER_ErrorCode ec = TALER_EC_NONE;
rctx->ev_sigs[i]
= TEH_keys_denomination_sign (
@@ -694,7 +695,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
rctx->rcds[i].coin_ev,
rctx->rcds[i].coin_ev_size,
&ec);
- if (NULL == rctx->ev_sigs[i].rsa_signature)
+ if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
ret = TALER_MHD_reply_with_ec (connection,
@@ -769,8 +770,7 @@ cleanup:
if (NULL != rctx->ev_sigs)
{
for (unsigned int i = 0; i<num_fresh_coins; i++)
- if (NULL != rctx->ev_sigs[i].rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature);
+ TALER_blinded_denom_sig_free (&rctx->ev_sigs[i]);
GNUNET_free (rctx->ev_sigs);
rctx->ev_sigs = NULL; /* just to be safe... */
}
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index 73b4b251..be8a88df 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -370,7 +370,7 @@ static MHD_RESULT
verify_and_execute_refund (struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Refund *refund)
{
- struct GNUNET_HashCode denom_hash;
+ struct TALER_DenominationHash denom_hash;
{
struct TALER_RefundRequestPS rr = {
diff --git a/src/exchange/taler-exchange-httpd_reserves_get.c b/src/exchange/taler-exchange-httpd_reserves_get.c
index 57ab7137..89a7dd49 100644
--- a/src/exchange/taler-exchange-httpd_reserves_get.c
+++ b/src/exchange/taler-exchange-httpd_reserves_get.c
@@ -26,6 +26,7 @@
#include "taler_mhd_lib.h"
#include "taler_json_lib.h"
#include "taler_dbevents.h"
+#include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_reserves_get.h"
#include "taler-exchange-httpd_responses.h"
@@ -152,11 +153,13 @@ db_event_cb (void *cls,
&old_scope);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Resuming from long-polling on reserve\n");
+ TEH_check_invariants ();
GNUNET_CONTAINER_DLL_remove (rp_head,
rp_tail,
rp);
MHD_resume_connection (rp->connection);
TALER_MHD_daemon_trigger ();
+ TEH_check_invariants ();
GNUNET_async_scope_restore (&old_scope);
}
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index 66da1216..cb1179d6 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -61,30 +61,25 @@ TEH_RESPONSE_compile_transaction_history (
{
const struct TALER_EXCHANGEDB_DepositListEntry *deposit =
pos->details.deposit;
- struct TALER_DepositRequestPS dr = {
- .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
- .purpose.size = htonl (sizeof (dr)),
- .h_contract_terms = deposit->h_contract_terms,
- .h_wire = deposit->h_wire,
- .h_denom_pub = deposit->h_denom_pub,
- .wallet_timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp),
- .refund_deadline = GNUNET_TIME_absolute_hton (
- deposit->refund_deadline),
- .merchant = deposit->merchant_pub,
- .coin_pub = *coin_pub
- };
+ struct TALER_MerchantWireHash h_wire;
- TALER_amount_hton (&dr.amount_with_fee,
- &deposit->amount_with_fee);
- TALER_amount_hton (&dr.deposit_fee,
- &deposit->deposit_fee);
+ TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
+ &deposit->wire_salt,
+ &h_wire);
#if ENABLE_SANITY_CHECKS
/* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
- &dr,
- &deposit->csig.eddsa_signature,
- &coin_pub->eddsa_pub))
+ TALER_wallet_deposit_verify (&deposit->amount_with_fee,
+ &deposit->deposit_fee,
+ &h_wire,
+ &deposit->h_contract_terms,
+ NULL /* h_extensions! */,
+ &deposit->h_denom_pub,
+ deposit->timestamp,
+ &deposit->merchant_pub,
+ deposit->refund_deadline,
+ coin_pub,
+ &deposit->csig))
{
GNUNET_break (0);
json_decref (history);
@@ -111,7 +106,7 @@ TEH_RESPONSE_compile_transaction_history (
GNUNET_JSON_pack_data_auto ("h_contract_terms",
&deposit->h_contract_terms),
GNUNET_JSON_pack_data_auto ("h_wire",
- &deposit->h_wire),
+ &h_wire),
GNUNET_JSON_pack_data_auto ("h_denom_pub",
&deposit->h_denom_pub),
GNUNET_JSON_pack_data_auto ("coin_sig",
@@ -419,7 +414,7 @@ TEH_RESPONSE_compile_transaction_history (
MHD_RESULT
TEH_RESPONSE_reply_unknown_denom_pub_hash (
struct MHD_Connection *connection,
- const struct GNUNET_HashCode *dph)
+ const struct TALER_DenominationHash *dph)
{
struct TALER_ExchangePublicKeyP epub;
struct TALER_ExchangeSignatureP esig;
@@ -466,7 +461,7 @@ TEH_RESPONSE_reply_unknown_denom_pub_hash (
MHD_RESULT
TEH_RESPONSE_reply_expired_denom_pub_hash (
struct MHD_Connection *connection,
- const struct GNUNET_HashCode *dph,
+ const struct TALER_DenominationHash *dph,
struct GNUNET_TIME_Absolute now,
enum TALER_ErrorCode ec,
const char *oper)
@@ -779,9 +774,8 @@ TEH_RESPONSE_compile_reserve_history (
&value);
TALER_amount_hton (&rcc.closing_fee,
&closing->closing_fee);
- GNUNET_CRYPTO_hash (closing->receiver_account_details,
- strlen (closing->receiver_account_details) + 1,
- &rcc.h_wire);
+ TALER_payto_hash (closing->receiver_account_details,
+ &rcc.h_payto);
if (TALER_EC_NONE !=
TEH_keys_exchange_sign (&rcc,
&pub,
diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h
index b46e85ce..09d566af 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -59,7 +59,7 @@ TEH_RESPONSE_compile_reserve_history (
MHD_RESULT
TEH_RESPONSE_reply_unknown_denom_pub_hash (
struct MHD_Connection *connection,
- const struct GNUNET_HashCode *dph);
+ const struct TALER_DenominationHash *dph);
/**
@@ -76,7 +76,7 @@ TEH_RESPONSE_reply_unknown_denom_pub_hash (
MHD_RESULT
TEH_RESPONSE_reply_expired_denom_pub_hash (
struct MHD_Connection *connection,
- const struct GNUNET_HashCode *dph,
+ const struct TALER_DenominationHash *dph,
struct GNUNET_TIME_Absolute now,
enum TALER_ErrorCode ec,
const char *oper);
diff --git a/src/exchange/taler-exchange-httpd_transfers_get.c b/src/exchange/taler-exchange-httpd_transfers_get.c
index abf0fdbc..e63acdc2 100644
--- a/src/exchange/taler-exchange-httpd_transfers_get.c
+++ b/src/exchange/taler-exchange-httpd_transfers_get.c
@@ -51,7 +51,7 @@ struct AggregatedDepositDetail
/**
* Hash of the contract terms.
*/
- struct GNUNET_HashCode h_contract_terms;
+ struct TALER_PrivateContractHash h_contract_terms;
/**
* Coin's public key of the deposited coin.
@@ -77,7 +77,7 @@ struct AggregatedDepositDetail
* @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 payto_uri 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
@@ -87,7 +87,7 @@ static MHD_RESULT
reply_transfer_details (struct MHD_Connection *connection,
const struct TALER_Amount *total,
const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode *h_wire,
+ const char *payto_uri,
const struct TALER_Amount *wire_fee,
struct GNUNET_TIME_Absolute exec_time,
const struct AggregatedDepositDetail *wdd_head)
@@ -145,7 +145,8 @@ reply_transfer_details (struct MHD_Connection *connection,
TALER_amount_hton (&wdp.wire_fee,
wire_fee);
wdp.merchant_pub = *merchant_pub;
- wdp.h_wire = *h_wire;
+ TALER_payto_hash (payto_uri,
+ &wdp.h_payto);
GNUNET_CRYPTO_hash_context_finish (hash_context,
&wdp.h_details);
{
@@ -172,8 +173,8 @@ reply_transfer_details (struct MHD_Connection *connection,
wire_fee),
GNUNET_JSON_pack_data_auto ("merchant_pub",
merchant_pub),
- GNUNET_JSON_pack_data_auto ("h_wire",
- h_wire),
+ GNUNET_JSON_pack_data_auto ("h_payto",
+ &wdp.h_payto),
GNUNET_JSON_pack_time_abs ("execution_time",
exec_time),
GNUNET_JSON_pack_array_steal ("deposits",
@@ -211,12 +212,6 @@ struct WtidTransactionContext
struct TALER_MerchantPublicKeyP merchant_pub;
/**
- * 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;
@@ -237,9 +232,9 @@ struct WtidTransactionContext
struct AggregatedDepositDetail *wdd_tail;
/**
- * Which method was used to wire the funds?
+ * Where were the funds wired?
*/
- char *wire_method;
+ char *payto_uri;
/**
* JSON array with details about the individual deposits.
@@ -253,7 +248,7 @@ struct WtidTransactionContext
* (as they should). Set to #GNUNET_SYSERR if we encountered an
* internal error.
*/
- int is_valid;
+ enum GNUNET_GenericReturnValue is_valid;
};
@@ -265,8 +260,7 @@ struct WtidTransactionContext
* @param cls our context for transmission
* @param rowid which row in the DB is the information from (for diagnostics), ignored
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
- * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
- * @param wire where the funds were sent
+ * @param account_payto_uri where the funds were sent
* @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 denom_pub denomination public key of the @a coin_pub (ignored)
@@ -278,35 +272,26 @@ static void
handle_deposit_data (void *cls,
uint64_t rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode *h_wire,
- const json_t *wire,
+ const char *account_payto_uri,
struct GNUNET_TIME_Absolute exec_time,
- const struct GNUNET_HashCode *h_contract_terms,
+ const struct TALER_PrivateContractHash *h_contract_terms,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *deposit_value,
const struct TALER_Amount *deposit_fee)
{
struct WtidTransactionContext *ctx = cls;
- char *wire_method;
(void) rowid;
(void) denom_pub;
if (GNUNET_SYSERR == ctx->is_valid)
return;
- if (NULL == (wire_method = TALER_JSON_wire_to_method (wire)))
- {
- GNUNET_break (0);
- ctx->is_valid = GNUNET_SYSERR;
- return;
- }
if (GNUNET_NO == ctx->is_valid)
{
/* First one we encounter, setup general information in 'ctx' */
ctx->merchant_pub = *merchant_pub;
- ctx->h_wire = *h_wire;
+ ctx->payto_uri = GNUNET_strdup (account_payto_uri);
ctx->exec_time = exec_time;
- ctx->wire_method = wire_method; /* captures the reference */
ctx->is_valid = GNUNET_YES;
if (0 >
TALER_amount_subtract (&ctx->total,
@@ -326,17 +311,13 @@ handle_deposit_data (void *cls,
(it should, otherwise the deposits should not have been aggregated) */
if ( (0 != GNUNET_memcmp (&ctx->merchant_pub,
merchant_pub)) ||
- (0 != strcmp (wire_method,
- ctx->wire_method)) ||
- (0 != GNUNET_memcmp (&ctx->h_wire,
- h_wire)) )
+ (0 != strcmp (account_payto_uri,
+ ctx->payto_uri)) )
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
- GNUNET_free (wire_method);
return;
}
- GNUNET_free (wire_method);
if (0 >
TALER_amount_subtract (&delta,
deposit_value,
@@ -389,8 +370,7 @@ free_ctx (struct WtidTransactionContext *ctx)
wdd);
GNUNET_free (wdd);
}
- GNUNET_free (ctx->wire_method);
- ctx->wire_method = NULL;
+ GNUNET_free (ctx->payto_uri);
}
@@ -458,14 +438,29 @@ get_transfer_deposits (void *cls,
NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
- ctx->wire_method,
- ctx->exec_time,
- &wire_fee_start_date,
- &wire_fee_end_date,
- &ctx->wire_fee,
- &closing_fee,
- &wire_fee_master_sig);
+ {
+ char *wire_method;
+
+ wire_method = TALER_payto_get_method (ctx->payto_uri);
+ if (NULL == wire_method)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ "payto:// without wire method encountered");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
+ wire_method,
+ ctx->exec_time,
+ &wire_fee_start_date,
+ &wire_fee_end_date,
+ &ctx->wire_fee,
+ &closing_fee,
+ &wire_fee_master_sig);
+ GNUNET_free (wire_method);
+ }
if (0 >= qs)
{
if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
@@ -530,7 +525,7 @@ TEH_handler_transfers_get (struct TEH_RequestContext *rc,
mhd_ret = reply_transfer_details (rc->connection,
&ctx.total,
&ctx.merchant_pub,
- &ctx.h_wire,
+ ctx.payto_uri,
&ctx.wire_fee,
ctx.exec_time,
ctx.wdd_head);
diff --git a/src/exchange/taler-exchange-httpd_wire.c b/src/exchange/taler-exchange-httpd_wire.c
index 8e4465ad..802204e3 100644
--- a/src/exchange/taler-exchange-httpd_wire.c
+++ b/src/exchange/taler-exchange-httpd_wire.c
@@ -22,6 +22,7 @@
#include <gnunet/gnunet_json_lib.h>
#include "taler_dbevents.h"
#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_wire.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
@@ -99,6 +100,9 @@ wire_update_event_cb (void *cls,
(void) cls;
(void) extra;
(void) extra_size;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Received /wire update event\n");
+ TEH_check_invariants ();
wire_generation++;
}
@@ -387,10 +391,12 @@ get_wire_state (void)
{
struct WireStateHandle *wsh;
+ TEH_check_invariants ();
wsh = build_wire_state ();
wire_state = wsh;
if (NULL != old_wsh)
destroy_wire_state (old_wsh);
+ TEH_check_invariants ();
return wsh;
}
return old_wsh;
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c
index d9cba045..d393567e 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2019 Taler Systems SA
+ Copyright (C) 2014-2021 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -107,7 +107,7 @@ struct WithdrawContext
/**
* Hash of the denomination public key.
*/
- struct GNUNET_HashCode denom_pub_hash;
+ struct TALER_DenominationHash denom_pub_hash;
/**
* Signature over the request.
@@ -129,10 +129,45 @@ struct WithdrawContext
*/
struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
+ /**
+ * KYC status for the operation.
+ */
+ struct TALER_EXCHANGEDB_KycStatus kyc;
+
+ /**
+ * Set to true if the operation was denied due to
+ * failing @e kyc checks.
+ */
+ bool kyc_denied;
+
};
/**
+ * Function called with another amount that was
+ * already withdrawn. Accumulates all amounts in
+ * @a cls.
+ *
+ * @param[in,out] cls a `struct TALER_Amount`
+ * @param val value to add to @a cls
+ */
+static void
+accumulate_withdraws (void *cls,
+ const struct TALER_Amount *val)
+{
+ struct TALER_Amount *acc = cls;
+
+ if (GNUNET_OK !=
+ TALER_amount_is_valid (acc))
+ return; /* ignore */
+ GNUNET_break (0 <=
+ TALER_amount_add (acc,
+ acc,
+ val));
+}
+
+
+/**
* Function implementing 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,
@@ -164,13 +199,15 @@ withdraw_transaction (void *cls,
struct WithdrawContext *wc = cls;
struct TALER_EXCHANGEDB_Reserve r;
enum GNUNET_DB_QueryStatus qs;
- struct TALER_DenominationSignature denom_sig;
+ struct TALER_BlindedDenominationSignature denom_sig;
#if OPTIMISTIC_SIGN
/* store away optimistic signature to protect
it from being overwritten by get_withdraw_info */
denom_sig = wc->collectable.sig;
- wc->collectable.sig.rsa_signature = NULL;
+ memset (&wc->collectable.sig,
+ 0,
+ sizeof (wc->collectable.sig));
#endif
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
&wc->wsrd.h_coin_envelope,
@@ -187,6 +224,10 @@ withdraw_transaction (void *cls,
return qs;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Asked to withdraw from %s amount of %s\n",
+ TALER_B2S (&wc->wsrd.reserve_pub),
+ TALER_amount2s (&wc->amount_required));
/* Don't sign again if we have already signed the coin */
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
@@ -194,7 +235,7 @@ withdraw_transaction (void *cls,
optimization trade-off loses in this case: we unnecessarily computed
a signature :-( */
#if OPTIMISTIC_SIGN
- GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
+ TALER_blinded_denom_sig_free (&denom_sig);
#endif
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -209,7 +250,8 @@ withdraw_transaction (void *cls,
"Trying to withdraw from reserve: %s\n",
TALER_B2S (&r.pub));
qs = TEH_plugin->reserves_get (TEH_plugin->cls,
- &r);
+ &r,
+ &wc->kyc);
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@@ -268,18 +310,78 @@ withdraw_transaction (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "KYC status is %s for %s\n",
+ wc->kyc.ok ? "ok" : "missing",
+ TALER_B2S (&r.pub));
+ if ( (! wc->kyc.ok) &&
+ (TEH_KYC_NONE != TEH_kyc_config.mode) &&
+ (TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
+ {
+ /* Wallet-to-wallet payments _always_ require KYC */
+ wc->kyc_denied = true;
+ return qs;
+ }
+ if ( (! wc->kyc.ok) &&
+ (TEH_KYC_NONE != TEH_kyc_config.mode) &&
+ (TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
+ (! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
+ {
+ /* Withdraws require KYC if above threshold */
+ struct TALER_Amount acc;
+ enum GNUNET_DB_QueryStatus qs2;
+
+ acc = wc->amount_required;
+ qs2 = TEH_plugin->select_withdraw_amounts_by_account (
+ TEH_plugin->cls,
+ &wc->wsrd.reserve_pub,
+ TEH_kyc_config.withdraw_period,
+ &accumulate_withdraws,
+ &acc);
+ if (0 > qs2)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs2)
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "withdraw details");
+ return qs2;
+ }
+
+ if (GNUNET_OK !=
+ TALER_amount_is_valid (&acc))
+ {
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_ec (connection,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Amount withdrawn so far is %s\n",
+ TALER_amount2s (&acc));
+ if (1 == /* 1: acc > withdraw_limit */
+ TALER_amount_cmp (&acc,
+ &TEH_kyc_config.withdraw_limit))
+ {
+ wc->kyc_denied = true;
+ return qs;
+ }
+ }
+
/* Balance is good, sign the coin! */
#if ! OPTIMISTIC_SIGN
if (NULL == wc->collectable.sig.rsa_signature)
{
- enum TALER_ErrorCode ec;
+ enum TALER_ErrorCode ec = TALER_EC_NONE;
wc->collectable.sig
= TEH_keys_denomination_sign (&wc->denom_pub_hash,
wc->blinded_msg,
wc->blinded_msg_len,
&ec);
- if (NULL == wc->collectable.sig.rsa_signature)
+ if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_ec (connection,
@@ -294,6 +396,10 @@ withdraw_transaction (void *cls,
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
wc->collectable.reserve_sig = wc->signature;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Persisting withdraw from %s over %s\n",
+ TALER_B2S (&r.pub),
+ TALER_amount2s (&wc->amount_required));
qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
&wc->collectable);
if (0 > qs)
@@ -329,6 +435,9 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
enum TALER_ErrorCode ec;
struct TEH_DenominationKey *dk;
+ memset (&wc,
+ 0,
+ sizeof (wc));
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
@@ -435,7 +544,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
wc.wsrd.h_denomination_pub
= wc.denom_pub_hash;
- GNUNET_CRYPTO_hash (wc.blinded_msg,
+ TALER_coin_ev_hash (wc.blinded_msg,
wc.blinded_msg_len,
&wc.wsrd.h_coin_envelope);
if (GNUNET_OK !=
@@ -455,12 +564,13 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
#if OPTIMISTIC_SIGN
/* Sign before transaction! */
+ ec = TALER_EC_NONE;
wc.collectable.sig
= TEH_keys_denomination_sign (&wc.denom_pub_hash,
wc.blinded_msg,
wc.blinded_msg_len,
&ec);
- if (NULL == wc.collectable.sig.rsa_signature)
+ if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
@@ -471,6 +581,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
#endif
/* run transaction and sign (if not optimistically signed before) */
+ wc.kyc_denied = false;
{
MHD_RESULT mhd_ret;
@@ -483,25 +594,34 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
{
/* Even if #withdraw_transaction() failed, it may have created a signature
(or we might have done it optimistically above). */
- if (NULL != wc.collectable.sig.rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
+ TALER_blinded_denom_sig_free (&wc.collectable.sig);
GNUNET_JSON_parse_free (spec);
return mhd_ret;
}
}
- /* Clean up and send back final (positive) response */
+ /* Clean up and send back final response */
GNUNET_JSON_parse_free (spec);
+ if (wc.kyc_denied)
+ {
+ TALER_blinded_denom_sig_free (&wc.collectable.sig);
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_ACCEPTED,
+ GNUNET_JSON_pack_uint64 ("payment_target_uuid",
+ wc.kyc.payment_target_uuid));
+ }
+
{
MHD_RESULT ret;
ret = TALER_MHD_REPLY_JSON_PACK (
rc->connection,
MHD_HTTP_OK,
- GNUNET_JSON_pack_rsa_signature ("ev_sig",
- wc.collectable.sig.rsa_signature));
- GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
+ TALER_JSON_pack_blinded_denom_sig ("ev_sig",
+ &wc.collectable.sig));
+ TALER_blinded_denom_sig_free (&wc.collectable.sig);
return ret;
}
}
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index eae03726..3145c3c0 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -15,13 +15,11 @@ pkgcfg_DATA = \
sqldir = $(prefix)/share/taler/sql/exchange/
sql_DATA = \
+ benchmark-0000.sql \
+ benchmark-0001.sql \
exchange-0000.sql \
exchange-0001.sql \
- exchange-0002.sql \
- exchange-0003.sql \
- drop0001.sql \
- drop0002.sql \
- drop0003.sql
+ drop0001.sql
EXTRA_DIST = \
exchangedb.conf \
@@ -29,6 +27,7 @@ EXTRA_DIST = \
plugin_exchangedb_common.c \
irbt_callbacks.c \
lrbt_callbacks.c \
+ bench-db-postgres.conf \
test-exchange-db-postgres.conf \
$(sql_DATA)
@@ -74,7 +73,8 @@ libtalerexchangedb_la_LDFLAGS = \
check_PROGRAMS = \
- test-exchangedb-postgres
+ test-exchangedb-postgres \
+ bench-db-postgres
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \
@@ -92,5 +92,15 @@ test_exchangedb_postgres_LDADD = \
-lgnunetutil \
$(XLIB)
+bench_db_postgres_SOURCES = \
+ bench_db.c
+bench_db_postgres_LDADD = \
+ libtalerexchangedb.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ -lgnunetpq \
+ -lgnunetutil \
+ $(XLIB)
+
EXTRA_test_exchangedb_postgres_DEPENDENCIES = \
libtaler_plugin_exchangedb_postgres.la
diff --git a/src/exchangedb/bench-db-postgres.conf b/src/exchangedb/bench-db-postgres.conf
new file mode 100644
index 00000000..af1a2981
--- /dev/null
+++ b/src/exchangedb/bench-db-postgres.conf
@@ -0,0 +1,7 @@
+# This file is in the public domain.
+#
+# Database-backend independent specification for the exchangedb module.
+#
+[bench-db-postgres]
+CONFIG = postgres://talercheck
+
diff --git a/src/exchangedb/bench_db.c b/src/exchangedb/bench_db.c
new file mode 100644
index 00000000..299b852d
--- /dev/null
+++ b/src/exchangedb/bench_db.c
@@ -0,0 +1,110 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2021 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchangedb/bench_db.c
+ * @brief test cases for DB interaction functions
+ * @author Sree Harsha Totakura
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include <gnunet/gnunet_pq_lib.h>
+
+/**
+ * Global result from the testcase.
+ */
+static int result;
+
+/**
+ * Initializes @a ptr with random data.
+ */
+#define RND_BLK(ptr) \
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr))
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with config
+ */
+static void
+run (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct GNUNET_PQ_Context *conn;
+
+ conn = GNUNET_PQ_connect_with_cfg (cfg,
+ "bench-db-postgres",
+ "benchmark-",
+ NULL,
+ NULL);
+ if (NULL == conn)
+ {
+ result = EXIT_FAILURE;
+ GNUNET_break (0);
+ return;
+ }
+
+ GNUNET_PQ_disconnect (conn);
+}
+
+
+int
+main (int argc,
+ char *const argv[])
+{
+ const char *plugin_name;
+ char *config_filename;
+ char *testname;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ (void) argc;
+ result = -1;
+ if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+ {
+ GNUNET_break (0);
+ return -1;
+ }
+ GNUNET_log_setup (argv[0],
+ "WARNING",
+ NULL);
+ plugin_name++;
+ (void) GNUNET_asprintf (&testname,
+ "bench-db-%s",
+ plugin_name);
+ (void) GNUNET_asprintf (&config_filename,
+ "%s.conf",
+ testname);
+ 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;
+ }
+ GNUNET_SCHEDULER_run (&run,
+ cfg);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free (config_filename);
+ GNUNET_free (testname);
+ return result;
+}
+
+
+/* end of bench_db.c */
diff --git a/src/exchangedb/benchmark-0000.sql b/src/exchangedb/benchmark-0000.sql
new file mode 100644
index 00000000..116f409b
--- /dev/null
+++ b/src/exchangedb/benchmark-0000.sql
@@ -0,0 +1,293 @@
+-- LICENSE AND COPYRIGHT
+--
+-- Copyright (C) 2010 Hubert depesz Lubaczewski
+--
+-- This program is distributed under the (Revised) BSD License:
+-- L<http://www.opensource.org/licenses/bsd-license.php>
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions
+-- are met:
+--
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+--
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- * Neither the name of Hubert depesz Lubaczewski's Organization
+-- nor the names of its contributors may be used to endorse or
+-- promote products derived from this software without specific
+-- prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+-- Code origin: https://gitlab.com/depesz/Versioning/blob/master/install.versioning.sql
+--
+--
+-- # NAME
+--
+-- **Versioning** - simplistic take on tracking and applying changes to databases.
+--
+-- # DESCRIPTION
+--
+-- This project strives to provide simple way to manage changes to
+-- database.
+--
+-- Instead of making changes on development server, then finding
+-- differences between production and development, deciding which ones
+-- should be installed on production, and finding a way to install them -
+-- you start with writing diffs themselves!
+--
+-- # INSTALLATION
+--
+-- To install versioning simply run install.versioning.sql in your database
+-- (all of them: production, stage, test, devel, ...).
+--
+-- # USAGE
+--
+-- In your files with patches to database, put whole logic in single
+-- transaction, and use \_v.\* functions - usually \_v.register_patch() at
+-- least to make sure everything is OK.
+--
+-- For example. Let's assume you have patch files:
+--
+-- ## 0001.sql:
+--
+-- ```
+-- create table users (id serial primary key, username text);
+-- ```
+--
+-- ## 0002.sql:
+--
+-- ```
+-- insert into users (username) values ('depesz');
+-- ```
+-- To change it to use versioning you would change the files, to this
+-- state:
+--
+-- 0000.sql:
+--
+-- ```
+-- BEGIN;
+-- select _v.register_patch('000-base', NULL, NULL);
+-- create table users (id serial primary key, username text);
+-- COMMIT;
+-- ```
+--
+-- ## 0002.sql:
+--
+-- ```
+-- BEGIN;
+-- select _v.register_patch('001-users', ARRAY['000-base'], NULL);
+-- insert into users (username) values ('depesz');
+-- COMMIT;
+-- ```
+--
+-- This will make sure that patch 001-users can only be applied after
+-- 000-base.
+--
+-- # AVAILABLE FUNCTIONS
+--
+-- ## \_v.register_patch( TEXT )
+--
+-- Registers named patch, or dies if it is already registered.
+--
+-- Returns integer which is id of patch in \_v.patches table - only if it
+-- succeeded.
+--
+-- ## \_v.register_patch( TEXT, TEXT[] )
+--
+-- Same as \_v.register_patch( TEXT ), but checks is all given patches (given as
+-- array in second argument) are already registered.
+--
+-- ## \_v.register_patch( TEXT, TEXT[], TEXT[] )
+--
+-- Same as \_v.register_patch( TEXT, TEXT[] ), but also checks if there are no conflicts with preexisting patches.
+--
+-- Third argument is array of names of patches that conflict with current one. So
+-- if any of them is installed - register_patch will error out.
+--
+-- ## \_v.unregister_patch( TEXT )
+--
+-- Removes information about given patch from the versioning data.
+--
+-- It doesn't remove objects that were created by this patch - just removes
+-- metainformation.
+--
+-- ## \_v.assert_user_is_superuser()
+--
+-- Make sure that current patch is being loaded by superuser.
+--
+-- If it's not - it will raise exception, and break transaction.
+--
+-- ## \_v.assert_user_is_not_superuser()
+--
+-- Make sure that current patch is not being loaded by superuser.
+--
+-- If it is - it will raise exception, and break transaction.
+--
+-- ## \_v.assert_user_is_one_of(TEXT, TEXT, ... )
+--
+-- Make sure that current patch is being loaded by one of listed users.
+--
+-- If ```current_user``` is not listed as one of arguments - function will raise
+-- exception and break the transaction.
+
+BEGIN;
+
+-- This file adds versioning support to database it will be loaded to.
+-- It requires that PL/pgSQL is already loaded - will raise exception otherwise.
+-- All versioning "stuff" (tables, functions) is in "_v" schema.
+
+-- All functions are defined as 'RETURNS SETOF INT4' to be able to make them to RETURN literally nothing (0 rows).
+-- >> RETURNS VOID<< IS similar, but it still outputs "empty line" in psql when calling.
+CREATE SCHEMA IF NOT EXISTS _v;
+COMMENT ON SCHEMA _v IS 'Schema for versioning data and functionality.';
+
+CREATE TABLE IF NOT EXISTS _v.patches (
+ patch_name TEXT PRIMARY KEY,
+ applied_tsz TIMESTAMPTZ NOT NULL DEFAULT now(),
+ applied_by TEXT NOT NULL,
+ requires TEXT[],
+ conflicts TEXT[]
+);
+COMMENT ON TABLE _v.patches IS 'Contains information about what patches are currently applied on database.';
+COMMENT ON COLUMN _v.patches.patch_name IS 'Name of patch, has to be unique for every patch.';
+COMMENT ON COLUMN _v.patches.applied_tsz IS 'When the patch was applied.';
+COMMENT ON COLUMN _v.patches.applied_by IS 'Who applied this patch (PostgreSQL username)';
+COMMENT ON COLUMN _v.patches.requires IS 'List of patches that are required for given patch.';
+COMMENT ON COLUMN _v.patches.conflicts IS 'List of patches that conflict with given patch.';
+
+CREATE OR REPLACE FUNCTION _v.register_patch( IN in_patch_name TEXT, IN in_requirements TEXT[], in_conflicts TEXT[], OUT versioning INT4 ) RETURNS setof INT4 AS $$
+DECLARE
+ t_text TEXT;
+ t_text_a TEXT[];
+ i INT4;
+BEGIN
+ -- Thanks to this we know only one patch will be applied at a time
+ LOCK TABLE _v.patches IN EXCLUSIVE MODE;
+
+ SELECT patch_name INTO t_text FROM _v.patches WHERE patch_name = in_patch_name;
+ IF FOUND THEN
+ RAISE EXCEPTION 'Patch % is already applied!', in_patch_name;
+ END IF;
+
+ t_text_a := ARRAY( SELECT patch_name FROM _v.patches WHERE patch_name = any( in_conflicts ) );
+ IF array_upper( t_text_a, 1 ) IS NOT NULL THEN
+ RAISE EXCEPTION 'Versioning patches conflict. Conflicting patche(s) installed: %.', array_to_string( t_text_a, ', ' );
+ END IF;
+
+ IF array_upper( in_requirements, 1 ) IS NOT NULL THEN
+ t_text_a := '{}';
+ FOR i IN array_lower( in_requirements, 1 ) .. array_upper( in_requirements, 1 ) LOOP
+ SELECT patch_name INTO t_text FROM _v.patches WHERE patch_name = in_requirements[i];
+ IF NOT FOUND THEN
+ t_text_a := t_text_a || in_requirements[i];
+ END IF;
+ END LOOP;
+ IF array_upper( t_text_a, 1 ) IS NOT NULL THEN
+ RAISE EXCEPTION 'Missing prerequisite(s): %.', array_to_string( t_text_a, ', ' );
+ END IF;
+ END IF;
+
+ INSERT INTO _v.patches (patch_name, applied_tsz, applied_by, requires, conflicts ) VALUES ( in_patch_name, now(), current_user, coalesce( in_requirements, '{}' ), coalesce( in_conflicts, '{}' ) );
+ RETURN;
+END;
+$$ language plpgsql;
+COMMENT ON FUNCTION _v.register_patch( TEXT, TEXT[], TEXT[] ) IS 'Function to register patches in database. Raises exception if there are conflicts, prerequisites are not installed or the migration has already been installed.';
+
+CREATE OR REPLACE FUNCTION _v.register_patch( TEXT, TEXT[] ) RETURNS setof INT4 AS $$
+ SELECT _v.register_patch( $1, $2, NULL );
+$$ language sql;
+COMMENT ON FUNCTION _v.register_patch( TEXT, TEXT[] ) IS 'Wrapper to allow registration of patches without conflicts.';
+CREATE OR REPLACE FUNCTION _v.register_patch( TEXT ) RETURNS setof INT4 AS $$
+ SELECT _v.register_patch( $1, NULL, NULL );
+$$ language sql;
+COMMENT ON FUNCTION _v.register_patch( TEXT ) IS 'Wrapper to allow registration of patches without requirements and conflicts.';
+
+CREATE OR REPLACE FUNCTION _v.unregister_patch( IN in_patch_name TEXT, OUT versioning INT4 ) RETURNS setof INT4 AS $$
+DECLARE
+ i INT4;
+ t_text_a TEXT[];
+BEGIN
+ -- Thanks to this we know only one patch will be applied at a time
+ LOCK TABLE _v.patches IN EXCLUSIVE MODE;
+
+ t_text_a := ARRAY( SELECT patch_name FROM _v.patches WHERE in_patch_name = ANY( requires ) );
+ IF array_upper( t_text_a, 1 ) IS NOT NULL THEN
+ RAISE EXCEPTION 'Cannot uninstall %, as it is required by: %.', in_patch_name, array_to_string( t_text_a, ', ' );
+ END IF;
+
+ DELETE FROM _v.patches WHERE patch_name = in_patch_name;
+ GET DIAGNOSTICS i = ROW_COUNT;
+ IF i < 1 THEN
+ RAISE EXCEPTION 'Patch % is not installed, so it can''t be uninstalled!', in_patch_name;
+ END IF;
+
+ RETURN;
+END;
+$$ language plpgsql;
+COMMENT ON FUNCTION _v.unregister_patch( TEXT ) IS 'Function to unregister patches in database. Dies if the patch is not registered, or if unregistering it would break dependencies.';
+
+CREATE OR REPLACE FUNCTION _v.assert_patch_is_applied( IN in_patch_name TEXT ) RETURNS TEXT as $$
+DECLARE
+ t_text TEXT;
+BEGIN
+ SELECT patch_name INTO t_text FROM _v.patches WHERE patch_name = in_patch_name;
+ IF NOT FOUND THEN
+ RAISE EXCEPTION 'Patch % is not applied!', in_patch_name;
+ END IF;
+ RETURN format('Patch %s is applied.', in_patch_name);
+END;
+$$ language plpgsql;
+COMMENT ON FUNCTION _v.assert_patch_is_applied( TEXT ) IS 'Function that can be used to make sure that patch has been applied.';
+
+CREATE OR REPLACE FUNCTION _v.assert_user_is_superuser() RETURNS TEXT as $$
+DECLARE
+ v_super bool;
+BEGIN
+ SELECT usesuper INTO v_super FROM pg_user WHERE usename = current_user;
+ IF v_super THEN
+ RETURN 'assert_user_is_superuser: OK';
+ END IF;
+ RAISE EXCEPTION 'Current user is not superuser - cannot continue.';
+END;
+$$ language plpgsql;
+COMMENT ON FUNCTION _v.assert_user_is_superuser() IS 'Function that can be used to make sure that patch is being applied using superuser account.';
+
+CREATE OR REPLACE FUNCTION _v.assert_user_is_not_superuser() RETURNS TEXT as $$
+DECLARE
+ v_super bool;
+BEGIN
+ SELECT usesuper INTO v_super FROM pg_user WHERE usename = current_user;
+ IF v_super THEN
+ RAISE EXCEPTION 'Current user is superuser - cannot continue.';
+ END IF;
+ RETURN 'assert_user_is_not_superuser: OK';
+END;
+$$ language plpgsql;
+COMMENT ON FUNCTION _v.assert_user_is_not_superuser() IS 'Function that can be used to make sure that patch is being applied using normal (not superuser) account.';
+
+CREATE OR REPLACE FUNCTION _v.assert_user_is_one_of(VARIADIC p_acceptable_users TEXT[] ) RETURNS TEXT as $$
+DECLARE
+BEGIN
+ IF current_user = any( p_acceptable_users ) THEN
+ RETURN 'assert_user_is_one_of: OK';
+ END IF;
+ RAISE EXCEPTION 'User is not one of: % - cannot continue.', p_acceptable_users;
+END;
+$$ language plpgsql;
+COMMENT ON FUNCTION _v.assert_user_is_one_of(TEXT[]) IS 'Function that can be used to make sure that patch is being applied by one of defined users.';
+
+COMMIT;
diff --git a/src/exchangedb/drop0003.sql b/src/exchangedb/benchmark-0001.sql
index fbdab04c..07fe4940 100644
--- a/src/exchangedb/drop0003.sql
+++ b/src/exchangedb/benchmark-0001.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2020 Taler Systems SA
+-- Copyright (C) 2014--2021 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
@@ -17,10 +17,15 @@
-- Everything in one big transaction
BEGIN;
--- Unregister patch (0003.sql)
-SELECT _v.unregister_patch('exchange-0003');
+-- Check patch versioning is in place.
+SELECT _v.register_patch('benchmark-0001', NULL, NULL);
-DROP TABLE IF EXISTS revolving_work_shards CASCADE;
--- And we're out of here...
+CREATE TABLE IF NOT EXISTS benchmap
+ (reserve_uuid BIGSERIAL UNIQUE
+ ,hash BYTEA PRIMARY KEY CHECK(LENGTH(hash)=64)
+ ,expiration_date INT8 NOT NULL
+ );
+
+-- Complete transaction
COMMIT;
diff --git a/src/exchangedb/drop0001.sql b/src/exchangedb/drop0001.sql
index f5a28192..52079e52 100644
--- a/src/exchangedb/drop0001.sql
+++ b/src/exchangedb/drop0001.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2020 Taler Systems SA
+-- Copyright (C) 2014--2021 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
@@ -22,14 +22,26 @@ BEGIN;
-- Unlike the other SQL files, it SHOULD be updated to reflect the
-- latest requirements for dropping tables.
--- Drops for 0001.sql
+-- Unregister patch (exchange-0001.sql)
+SELECT _v.unregister_patch('exchange-0001');
+
+-- Drops for exchange-0001.sql
+DROP TABLE IF EXISTS revolving_work_shards CASCADE;
+DROP TABLE IF EXISTS auditors CASCADE;
+DROP TABLE IF EXISTS auditor_denom_sigs CASCADE;
+DROP TABLE IF EXISTS exchange_sign_keys CASCADE;
+DROP TABLE IF EXISTS wire_accounts CASCADE;
+DROP TABLE IF EXISTS signkey_revocations CASCADE;
+DROP TABLE IF EXISTS work_shards CASCADE;
DROP TABLE IF EXISTS prewire CASCADE;
DROP TABLE IF EXISTS recoup CASCADE;
DROP TABLE IF EXISTS recoup_refresh CASCADE;
DROP TABLE IF EXISTS aggregation_tracking CASCADE;
DROP TABLE IF EXISTS wire_out CASCADE;
+DROP TABLE IF EXISTS wire_targets CASCADE;
DROP TABLE IF EXISTS wire_fee CASCADE;
DROP TABLE IF EXISTS deposits CASCADE;
+DROP TABLE IF EXISTS extension_details CASCADE;
DROP TABLE IF EXISTS refunds CASCADE;
DROP TABLE IF EXISTS refresh_commitments CASCADE;
DROP TABLE IF EXISTS refresh_revealed_coins CASCADE;
@@ -42,8 +54,6 @@ DROP TABLE IF EXISTS reserves CASCADE;
DROP TABLE IF EXISTS denomination_revocations CASCADE;
DROP TABLE IF EXISTS denominations CASCADE;
--- Unregister patch (0001.sql)
-SELECT _v.unregister_patch('exchange-0001');
-- And we're out of here...
COMMIT;
diff --git a/src/exchangedb/drop0002.sql b/src/exchangedb/drop0002.sql
deleted file mode 100644
index 12db64c5..00000000
--- a/src/exchangedb/drop0002.sql
+++ /dev/null
@@ -1,31 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2020 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
-
--- Everything in one big transaction
-BEGIN;
-
--- Unregister patch (0002.sql)
-SELECT _v.unregister_patch('exchange-0002');
-
-DROP TABLE IF EXISTS auditors CASCADE;
-DROP TABLE IF EXISTS auditor_denom_sigs CASCADE;
-DROP TABLE IF EXISTS exchange_sign_keys CASCADE;
-DROP TABLE IF EXISTS wire_accounts CASCADE;
-DROP TABLE IF EXISTS signkey_revocations CASCADE;
-DROP TABLE IF EXISTS work_shards CASCADE;
-
--- And we're out of here...
-COMMIT;
diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql
index c8438d5c..4a6c9afb 100644
--- a/src/exchangedb/exchange-0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2020 Taler Systems SA
+-- Copyright (C) 2014--2021 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
@@ -22,9 +22,11 @@ SELECT _v.register_patch('exchange-0001', NULL, NULL);
CREATE TABLE IF NOT EXISTS denominations
- (denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
+ (denominations_serial BIGSERIAL UNIQUE
+ ,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
+ ,denom_type INT4 NOT NULL DEFAULT (1) -- 1 == RSA (for now, remove default later!)
+ ,age_restrictions INT4 NOT NULL DEFAULT (0)
,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
@@ -43,6 +45,12 @@ CREATE TABLE IF NOT EXISTS denominations
);
COMMENT ON TABLE denominations
IS 'Main denominations table. All the valid denominations the exchange knows about.';
+COMMENT ON COLUMN denominations.denom_type
+ IS 'determines cipher type for blind signatures used with this denomination; 0 is for RSA';
+COMMENT ON COLUMN denominations.age_restrictions
+ IS 'bitmask with the age restrictions that are being used for this denomination; 0 if denomination does not support the use of age restrictions';
+COMMENT ON COLUMN denominations.denominations_serial
+ IS 'needed for exchange-auditor replication logic';
CREATE INDEX IF NOT EXISTS denominations_expire_legal_index
ON denominations
@@ -51,16 +59,36 @@ CREATE INDEX IF NOT EXISTS denominations_expire_legal_index
CREATE TABLE IF NOT EXISTS denomination_revocations
(denom_revocations_serial_id BIGSERIAL UNIQUE
- ,denom_pub_hash BYTEA PRIMARY KEY REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE
+ ,denominations_serial INT8 PRIMARY KEY REFERENCES denominations (denominations_serial) ON DELETE CASCADE
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
);
COMMENT ON TABLE denomination_revocations
IS 'remembering which denomination keys have been revoked';
+CREATE TABLE IF NOT EXISTS wire_targets
+(wire_target_serial_id BIGSERIAL UNIQUE
+,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64)
+,payto_uri VARCHAR NOT NULL
+,kyc_ok BOOLEAN NOT NULL DEFAULT (FALSE)
+,external_id VARCHAR
+,PRIMARY KEY (h_payto)
+);
+COMMENT ON TABLE wire_targets
+ IS 'All recipients of money via the exchange';
+COMMENT ON COLUMN wire_targets.payto_uri
+ IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
+COMMENT ON COLUMN wire_targets.h_payto
+ IS 'Unsalted hash of payto_uri';
+COMMENT ON COLUMN wire_targets.kyc_ok
+ IS 'true if the KYC check was passed successfully';
+COMMENT ON COLUMN wire_targets.external_id
+ IS 'Name of the user that was used for OAuth 2.0-based legitimization';
+
+
CREATE TABLE IF NOT EXISTS reserves
- (reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)
- ,account_details TEXT NOT NULL
+ (reserve_uuid BIGSERIAL UNIQUE
+ ,reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)
,current_balance_val INT8 NOT NULL
,current_balance_frac INT4 NOT NULL
,expiration_date INT8 NOT NULL
@@ -73,7 +101,6 @@ COMMENT ON COLUMN reserves.expiration_date
COMMENT ON COLUMN reserves.gc_date
IS 'Used to forget all information about a reserve during garbage collection';
-
CREATE INDEX IF NOT EXISTS reserves_expiration_index
ON reserves
(expiration_date
@@ -92,18 +119,19 @@ COMMENT ON INDEX reserves_gc_index
CREATE TABLE IF NOT EXISTS reserves_in
(reserve_in_serial_id BIGSERIAL UNIQUE
- ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE
+ ,reserve_uuid INT8 NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
,wire_reference INT8 NOT NULL
,credit_val INT8 NOT NULL
,credit_frac INT4 NOT NULL
- ,sender_account_details TEXT NOT NULL
+ ,wire_source_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id)
,exchange_account_section TEXT NOT NULL
,execution_date INT8 NOT NULL
- ,PRIMARY KEY (reserve_pub, wire_reference)
+ ,PRIMARY KEY (reserve_uuid, wire_reference)
);
COMMENT ON TABLE reserves_in
IS 'list of transfers of funds into the reserves, one per incoming wire transfer';
--- FIXME: explain 'wire_reference'!
+COMMENT ON COLUMN reserves_in.wire_source_serial_id
+ IS 'Identifies the debited bank account and KYC status';-- FIXME: explain 'wire_reference'!
CREATE INDEX IF NOT EXISTS reserves_in_execution_index
ON reserves_in
(exchange_account_section
@@ -118,28 +146,30 @@ CREATE INDEX IF NOT EXISTS reserves_in_exchange_account_serial
CREATE TABLE IF NOT EXISTS reserves_close
(close_uuid BIGSERIAL PRIMARY KEY
- ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE
+ ,reserve_uuid INT8 NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
,execution_date INT8 NOT NULL
,wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)
- ,receiver_account TEXT NOT NULL
+ ,wire_target_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id)
,amount_val INT8 NOT NULL
,amount_frac INT4 NOT NULL
,closing_fee_val INT8 NOT NULL
,closing_fee_frac INT4 NOT NULL);
COMMENT ON TABLE reserves_close
IS 'wire transfers executed by the reserve to close reserves';
+COMMENT ON COLUMN reserves_close.wire_target_serial_id
+ IS 'Identifies the credited bank account (and KYC status). Note that closing does not depend on KYC.';
-CREATE INDEX IF NOT EXISTS reserves_close_by_reserve
+CREATE INDEX IF NOT EXISTS reserves_close_by_uuid
ON reserves_close
- (reserve_pub);
+ (reserve_uuid);
CREATE TABLE IF NOT EXISTS reserves_out
(reserve_out_serial_id BIGSERIAL UNIQUE
,h_blind_ev BYTEA PRIMARY KEY CHECK (LENGTH(h_blind_ev)=64)
- ,denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash)
+ ,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial)
,denom_sig BYTEA NOT NULL
- ,reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE
+ ,reserve_uuid INT8 NOT NULL REFERENCES reserves (reserve_uuid) 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
@@ -149,42 +179,121 @@ COMMENT ON TABLE reserves_out
IS 'Withdraw operations performed on reserves.';
COMMENT ON COLUMN reserves_out.h_blind_ev
IS 'Hash of the blinded coin, used as primary key here so that broken clients that use a non-random coin or blinding factor fail to withdraw (otherwise they would fail on deposit when the coin is not unique there).';
-COMMENT ON COLUMN reserves_out.denom_pub_hash
+COMMENT ON COLUMN reserves_out.denominations_serial
IS 'We do not CASCADE ON DELETE here, we may keep the denomination data alive';
-CREATE INDEX IF NOT EXISTS reserves_out_reserve_pub_index
+CREATE INDEX IF NOT EXISTS reserves_out_reserve_uuid_index
ON reserves_out
- (reserve_pub);
-COMMENT ON INDEX reserves_out_reserve_pub_index
+ (reserve_uuid);
+COMMENT ON INDEX reserves_out_reserve_uuid_index
IS 'for get_reserves_out';
+
CREATE INDEX IF NOT EXISTS reserves_out_execution_date
ON reserves_out
(execution_date);
+
CREATE INDEX IF NOT EXISTS reserves_out_for_get_withdraw_info
ON reserves_out
- (denom_pub_hash
+ (denominations_serial
,h_blind_ev
);
+CREATE TABLE IF NOT EXISTS auditors
+ (auditor_uuid BIGSERIAL UNIQUE
+ ,auditor_pub BYTEA PRIMARY KEY CHECK (LENGTH(auditor_pub)=32)
+ ,auditor_name VARCHAR NOT NULL
+ ,auditor_url VARCHAR NOT NULL
+ ,is_active BOOLEAN NOT NULL
+ ,last_change INT8 NOT NULL
+ );
+COMMENT ON TABLE auditors
+ IS 'Table with auditors the exchange uses or has used in the past. Entries never expire as we need to remember the last_change column indefinitely.';
+COMMENT ON COLUMN auditors.auditor_pub
+ IS 'Public key of the auditor.';
+COMMENT ON COLUMN auditors.auditor_url
+ IS 'The base URL of the auditor.';
+COMMENT ON COLUMN auditors.is_active
+ IS 'true if we are currently supporting the use of this auditor.';
+COMMENT ON COLUMN auditors.last_change
+ IS 'Latest time when active status changed. Used to detect replays of old messages.';
+
+
+CREATE TABLE IF NOT EXISTS auditor_denom_sigs
+ (auditor_denom_serial BIGSERIAL UNIQUE
+ ,auditor_uuid INT8 NOT NULL REFERENCES auditors (auditor_uuid) ON DELETE CASCADE
+ ,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial) ON DELETE CASCADE
+ ,auditor_sig BYTEA CHECK (LENGTH(auditor_sig)=64)
+ ,PRIMARY KEY (denominations_serial, auditor_uuid)
+ );
+COMMENT ON TABLE auditor_denom_sigs
+ IS 'Table with auditor signatures on exchange denomination keys.';
+COMMENT ON COLUMN auditor_denom_sigs.auditor_uuid
+ IS 'Identifies the auditor.';
+COMMENT ON COLUMN auditor_denom_sigs.denominations_serial
+ IS 'Denomination the signature is for.';
+COMMENT ON COLUMN auditor_denom_sigs.auditor_sig
+ IS 'Signature of the auditor, of purpose TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS.';
+
+
+CREATE TABLE IF NOT EXISTS exchange_sign_keys
+ (esk_serial BIGSERIAL UNIQUE
+ ,exchange_pub BYTEA PRIMARY KEY CHECK (LENGTH(exchange_pub)=32)
+ ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
+ ,valid_from INT8 NOT NULL
+ ,expire_sign INT8 NOT NULL
+ ,expire_legal INT8 NOT NULL
+ );
+COMMENT ON TABLE exchange_sign_keys
+ IS 'Table with master public key signatures on exchange online signing keys.';
+COMMENT ON COLUMN exchange_sign_keys.exchange_pub
+ IS 'Public online signing key of the exchange.';
+COMMENT ON COLUMN exchange_sign_keys.master_sig
+ IS 'Signature affirming the validity of the signing key of purpose TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY.';
+COMMENT ON COLUMN exchange_sign_keys.valid_from
+ IS 'Time when this online signing key will first be used to sign messages.';
+COMMENT ON COLUMN exchange_sign_keys.expire_sign
+ IS 'Time when this online signing key will no longer be used to sign.';
+COMMENT ON COLUMN exchange_sign_keys.expire_legal
+ IS 'Time when this online signing key legally expires.';
+
+
+CREATE TABLE IF NOT EXISTS signkey_revocations
+ (signkey_revocations_serial_id BIGSERIAL UNIQUE
+ ,esk_serial INT8 PRIMARY KEY REFERENCES exchange_sign_keys (esk_serial) ON DELETE CASCADE
+ ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
+ );
+COMMENT ON TABLE signkey_revocations
+ IS 'remembering which online signing keys have been revoked';
+
CREATE TABLE IF NOT EXISTS known_coins
(known_coin_id BIGSERIAL UNIQUE
,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
+ ,age_hash BYTEA CHECK (LENGTH(age_hash)=32)
+ ,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial) ON DELETE CASCADE
,denom_sig BYTEA NOT NULL
);
COMMENT ON TABLE known_coins
IS 'information about coins and their signatures, so we do not have to store the signatures more than once if a coin is involved in multiple operations';
+COMMENT ON COLUMN known_coins.coin_pub
+ IS 'EdDSA public key of the coin';
+COMMENT ON COLUMN known_coins.age_hash
+ IS 'Optional hash for age restrictions as per DD 24 (active if denom_type has the respective bit set)';
+COMMENT ON COLUMN known_coins.denom_sig
+ IS 'This is the signature of the exchange that affirms that the coin is a valid coin. The specific signature type depends on denom_type of the denomination.';
CREATE INDEX IF NOT EXISTS known_coins_by_denomination
ON known_coins
- (denom_pub_hash);
+ (denominations_serial);
+CREATE INDEX IF NOT EXISTS known_coins_by_hashed_coin_pub
+ ON known_coins
+ USING HASH (coin_pub);
CREATE TABLE IF NOT EXISTS refresh_commitments
(melt_serial_id BIGSERIAL UNIQUE
,rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64)
- ,old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE
+ ,old_known_coin_id INT8 NOT NULL REFERENCES known_coins (known_coin_id) 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
@@ -193,26 +302,32 @@ CREATE TABLE IF NOT EXISTS refresh_commitments
COMMENT ON TABLE refresh_commitments
IS 'Commitments made when melting coins and the gamma value chosen by the exchange.';
-CREATE INDEX IF NOT EXISTS refresh_commitments_old_coin_pub_index
+CREATE INDEX IF NOT EXISTS refresh_commitments_old_coin_id_index
ON refresh_commitments
- (old_coin_pub);
+ (old_known_coin_id);
+CREATE INDEX IF NOT EXISTS known_coins_by_hashed_rc
+ ON refresh_commitments
+ USING HASH (rc);
CREATE TABLE IF NOT EXISTS refresh_revealed_coins
- (rc BYTEA NOT NULL REFERENCES refresh_commitments (rc) ON DELETE CASCADE
+ (rrc_serial BIGSERIAL UNIQUE
+ ,melt_serial_id INT8 NOT NULL REFERENCES refresh_commitments (melt_serial_id) ON DELETE CASCADE
,freshcoin_index INT4 NOT NULL
,link_sig BYTEA NOT NULL CHECK(LENGTH(link_sig)=64)
- ,denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE
+ ,denominations_serial INT8 NOT NULL REFERENCES denominations (denominations_serial) ON DELETE CASCADE
,coin_ev BYTEA UNIQUE NOT NULL
,h_coin_ev BYTEA NOT NULL CHECK(LENGTH(h_coin_ev)=64)
,ev_sig BYTEA NOT NULL
- ,PRIMARY KEY (rc, freshcoin_index)
+ ,PRIMARY KEY (melt_serial_id, freshcoin_index)
,UNIQUE (h_coin_ev)
);
COMMENT ON TABLE refresh_revealed_coins
IS 'Revelations about the new coins that are to be created during a melting session.';
-COMMENT ON COLUMN refresh_revealed_coins.rc
- IS 'refresh commitment identifying the melt operation';
+COMMENT ON COLUMN refresh_revealed_coins.rrc_serial
+ IS 'needed for exchange-auditor replication logic';
+COMMENT ON COLUMN refresh_revealed_coins.melt_serial_id
+ IS 'Identifies the refresh commitment (rc) of the melt operation.';
COMMENT ON COLUMN refresh_revealed_coins.freshcoin_index
IS 'index of the fresh coin being created (one melt operation may result in multiple fresh coins)';
COMMENT ON COLUMN refresh_revealed_coins.coin_ev
@@ -222,20 +337,23 @@ COMMENT ON COLUMN refresh_revealed_coins.h_coin_ev
COMMENT ON COLUMN refresh_revealed_coins.ev_sig
IS 'exchange signature over the envelope';
-CREATE INDEX IF NOT EXISTS refresh_revealed_coins_coin_pub_index
+CREATE INDEX IF NOT EXISTS refresh_revealed_coins_denominations_index
ON refresh_revealed_coins
- (denom_pub_hash);
+ (denominations_serial);
CREATE TABLE IF NOT EXISTS refresh_transfer_keys
- (rc BYTEA NOT NULL PRIMARY KEY REFERENCES refresh_commitments (rc) ON DELETE CASCADE
+ (rtc_serial BIGSERIAL UNIQUE
+ ,melt_serial_id INT8 PRIMARY KEY REFERENCES refresh_commitments (melt_serial_id) ON DELETE CASCADE
,transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)
,transfer_privs BYTEA NOT NULL
);
COMMENT ON TABLE refresh_transfer_keys
IS 'Transfer keys of a refresh operation (the data revealed to the exchange).';
-COMMENT ON COLUMN refresh_transfer_keys.rc
- IS 'refresh commitment identifying the melt operation';
+COMMENT ON COLUMN refresh_transfer_keys.rtc_serial
+ IS 'needed for exchange-auditor replication logic';
+COMMENT ON COLUMN refresh_transfer_keys.melt_serial_id
+ IS 'Identifies the refresh commitment (rc) of the operation.';
COMMENT ON COLUMN refresh_transfer_keys.transfer_pub
IS 'transfer public key for the gamma index';
COMMENT ON COLUMN refresh_transfer_keys.transfer_privs
@@ -243,16 +361,25 @@ COMMENT ON COLUMN refresh_transfer_keys.transfer_privs
CREATE INDEX IF NOT EXISTS refresh_transfer_keys_coin_tpub
ON refresh_transfer_keys
- (rc
+ (melt_serial_id
,transfer_pub
);
COMMENT ON INDEX refresh_transfer_keys_coin_tpub
IS 'for get_link (unsure if this helps or hurts for performance as there should be very few transfer public keys per rc, but at least in theory this helps the ORDER BY clause)';
+CREATE TABLE IF NOT EXISTS extension_details
+ (extension_details_serial_id BIGSERIAL PRIMARY KEY
+ ,extension_options VARCHAR);
+COMMENT ON TABLE extension_details
+ IS 'Extensions that were provided with deposits (not yet used).';
+COMMENT ON COLUMN extension_details.extension_options
+ IS 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the extensions supported by the exchange.';
+
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
+ ,shard INT8 NOT NULL DEFAULT 0
+ ,known_coin_id INT8 NOT NULL REFERENCES known_coins (known_coin_id) ON DELETE CASCADE
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
,wallet_timestamp INT8 NOT NULL
@@ -261,23 +388,34 @@ CREATE TABLE IF NOT EXISTS deposits
,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
+ ,wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)
+ ,wire_target_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id)
,tiny BOOLEAN NOT NULL DEFAULT FALSE
,done BOOLEAN NOT NULL DEFAULT FALSE
- ,UNIQUE (coin_pub, merchant_pub, h_contract_terms)
+ ,extension_blocked BOOLEAN NOT NULL DEFAULT FALSE
+ ,extension_details_serial_id INT8 REFERENCES extension_details (extension_details_serial_id)
+ ,UNIQUE (known_coin_id, merchant_pub, h_contract_terms)
);
COMMENT ON TABLE deposits
IS 'Deposits we have received and for which we need to make (aggregate) wire transfers (and manage refunds).';
+COMMENT ON COLUMN deposits.shard
+ IS 'Used for load sharding. Should be set based on h_payto and merchant_pub. Default of 0 onlyapplies for columns migrated from a previous version without sharding support. 64-bit value because we need an *unsigned* 32-bit value.';
+COMMENT ON COLUMN deposits.wire_target_serial_id
+ IS 'Identifies the target bank account and KYC status';COMMENT ON COLUMN deposits.wire_salt
+ IS 'Salt used when hashing the payto://-URI to get the h_wire';
COMMENT ON COLUMN deposits.done
IS 'Set to TRUE once we have included this deposit in some aggregate wire transfer to the merchant';
+COMMENT ON COLUMN deposits.extension_blocked
+ IS 'True if the aggregation of the deposit is currently blocked by some extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.';
+COMMENT ON COLUMN deposits.extension_details_serial_id
+ IS 'References extensions table, NULL if extensions are not used';
COMMENT ON COLUMN deposits.tiny
IS 'Set to TRUE if we decided that the amount is too small to ever trigger a wire transfer by itself (requires real aggregation)';
CREATE INDEX IF NOT EXISTS deposits_coin_pub_merchant_contract_index
ON deposits
- (coin_pub
+ (known_coin_id
,merchant_pub
,h_contract_terms
);
@@ -285,18 +423,21 @@ COMMENT ON INDEX deposits_coin_pub_merchant_contract_index
IS 'for get_deposit_for_wtid and test_deposit_done';
CREATE INDEX IF NOT EXISTS deposits_get_ready_index
ON deposits
- (tiny
- ,done
+ (shard
,wire_deadline
,refund_deadline
+ ,tiny
+ ,done
+ ,extension_blocked
);
COMMENT ON INDEX deposits_coin_pub_merchant_contract_index
IS 'for deposits_get_ready';
CREATE INDEX IF NOT EXISTS deposits_iterate_matching_index
ON deposits
(merchant_pub
- ,h_wire
+ ,wire_target_serial_id
,done
+ ,extension_blocked
,wire_deadline
);
COMMENT ON INDEX deposits_iterate_matching_index
@@ -305,30 +446,26 @@ COMMENT ON INDEX deposits_iterate_matching_index
CREATE TABLE IF NOT EXISTS refunds
(refund_serial_id BIGSERIAL UNIQUE
- ,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE
- ,merchant_pub BYTEA NOT NULL CHECK(LENGTH(merchant_pub)=32)
+ ,deposit_serial_id INT8 NOT NULL REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE
,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
- ,PRIMARY KEY (coin_pub, merchant_pub, h_contract_terms, rtransaction_id)
+ ,PRIMARY KEY (deposit_serial_id, rtransaction_id)
);
COMMENT ON TABLE refunds
IS 'Data on coins that were refunded. Technically, refunds always apply against specific deposit operations involving a coin. The combination of coin_pub, merchant_pub, h_contract_terms and rtransaction_id MUST be unique, and we usually select by coin_pub so that one goes first.';
+COMMENT ON COLUMN refunds.deposit_serial_id
+ IS 'Identifies ONLY the merchant_pub, h_contract_terms and known_coin_id. Multiple deposits may match a refund, this only identifies one of them.';
COMMENT ON COLUMN refunds.rtransaction_id
IS 'used by the merchant to make refunds unique in case the same coin for the same deposit gets a subsequent (higher) refund';
-CREATE INDEX IF NOT EXISTS refunds_coin_pub_index
- ON refunds
- (coin_pub);
-
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)=32)
- ,wire_target TEXT NOT NULL
+ ,wire_target_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id)
,exchange_account_section TEXT NOT NULL
,amount_val INT8 NOT NULL
,amount_frac INT4 NOT NULL
@@ -337,6 +474,8 @@ COMMENT ON TABLE wire_out
IS 'wire transfers the exchange has executed';
COMMENT ON COLUMN wire_out.exchange_account_section
IS 'identifies the configuration section with the debit account of this payment';
+COMMENT ON COLUMN wire_out.wire_target_serial_id
+ IS 'Identifies the credited bank account and KYC status';
CREATE TABLE IF NOT EXISTS aggregation_tracking
(aggregation_serial_id BIGSERIAL UNIQUE
@@ -356,7 +495,8 @@ COMMENT ON INDEX aggregation_tracking_wtid_index
CREATE TABLE IF NOT EXISTS wire_fee
- (wire_method VARCHAR NOT NULL
+ (wire_fee_serial BIGSERIAL UNIQUE
+ ,wire_method VARCHAR NOT NULL
,start_date INT8 NOT NULL
,end_date INT8 NOT NULL
,wire_fee_val INT8 NOT NULL
@@ -368,6 +508,8 @@ CREATE TABLE IF NOT EXISTS wire_fee
);
COMMENT ON TABLE wire_fee
IS 'list of the wire fees of this exchange, by date';
+COMMENT ON COLUMN wire_fee.wire_fee_serial
+ IS 'needed for exchange-auditor replication logic';
CREATE INDEX IF NOT EXISTS wire_fee_gc_index
ON wire_fee
@@ -376,60 +518,53 @@ CREATE INDEX IF NOT EXISTS wire_fee_gc_index
CREATE TABLE IF NOT EXISTS recoup
(recoup_uuid BIGSERIAL UNIQUE
- ,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub)
+ ,known_coin_id INT8 NOT NULL REFERENCES known_coins (known_coin_id)
,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
,timestamp INT8 NOT NULL
- ,h_blind_ev BYTEA NOT NULL REFERENCES reserves_out (h_blind_ev) ON DELETE CASCADE
+ ,reserve_out_serial_id INT8 NOT NULL REFERENCES reserves_out (reserve_out_serial_id) ON DELETE CASCADE
);
COMMENT ON TABLE recoup
IS 'Information about recoups that were executed';
-COMMENT ON COLUMN recoup.coin_pub
- IS 'Do not CASCADE ON DROP on the coin_pub, as we may keep the coin alive!';
+COMMENT ON COLUMN recoup.known_coin_id
+ IS 'Do not CASCADE ON DROP on the known_coin_id, as we may keep the coin alive!';
+COMMENT ON COLUMN recoup.reserve_out_serial_id
+ IS 'Identifies the h_blind_ev of the recouped coin.';
--- Note: this first index is redundant;
--- It is implicitly removed by the exchange-0002.sql
--- schema changes.
-CREATE INDEX IF NOT EXISTS recoup_by_coin_index
- ON recoup
- (coin_pub);
CREATE INDEX IF NOT EXISTS recoup_by_h_blind_ev
ON recoup
- (h_blind_ev);
+ (reserve_out_serial_id);
CREATE INDEX IF NOT EXISTS recoup_for_by_reserve
ON recoup
- (coin_pub
- ,h_blind_ev
+ (known_coin_id
+ ,reserve_out_serial_id
);
CREATE TABLE IF NOT EXISTS recoup_refresh
(recoup_refresh_uuid BIGSERIAL UNIQUE
- ,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub)
+ ,known_coin_id INT8 NOT NULL REFERENCES known_coins (known_coin_id)
,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
,timestamp INT8 NOT NULL
- ,h_blind_ev BYTEA NOT NULL REFERENCES refresh_revealed_coins (h_coin_ev) ON DELETE CASCADE
+ ,rrc_serial INT8 NOT NULL UNIQUE REFERENCES refresh_revealed_coins (rrc_serial) ON DELETE CASCADE
);
-COMMENT ON COLUMN recoup_refresh.coin_pub
- IS 'Do not CASCADE ON DROP on the coin_pub, as we may keep the coin alive!';
+COMMENT ON COLUMN recoup_refresh.known_coin_id
+ IS 'Do not CASCADE ON DROP on the known_coin_id, as we may keep the coin alive!';
+COMMENT ON COLUMN recoup_refresh.rrc_serial
+ IS 'Identifies the h_blind_ev of the recouped coin (as h_coin_ev).';
--- Note: this index is redundant; implicitly removed
--- by the exchange-0002.sql update!
-CREATE INDEX IF NOT EXISTS recoup_refresh_by_coin_index
- ON recoup_refresh
- (coin_pub);
CREATE INDEX IF NOT EXISTS recoup_refresh_by_h_blind_ev
ON recoup_refresh
- (h_blind_ev);
+ (rrc_serial);
CREATE INDEX IF NOT EXISTS recoup_refresh_for_by_reserve
ON recoup_refresh
- (coin_pub
- ,h_blind_ev
+ (known_coin_id
+ ,rrc_serial
);
@@ -437,16 +572,115 @@ CREATE TABLE IF NOT EXISTS prewire
(prewire_uuid BIGSERIAL PRIMARY KEY
,type TEXT NOT NULL
,finished BOOLEAN NOT NULL DEFAULT false
+ ,failed BOOLEAN NOT NULL DEFAULT false
,buf BYTEA NOT NULL
);
COMMENT ON TABLE prewire
IS 'pre-commit data for wire transfers we are about to execute';
+COMMENT ON COLUMN prewire.failed
+ IS 'set to TRUE if the bank responded with a non-transient failure to our transfer request';
+COMMENT ON COLUMN prewire.finished
+ IS 'set to TRUE once bank confirmed receiving the wire transfer request';
+COMMENT ON COLUMN prewire.buf
+ IS 'serialized data to send to the bank to execute the wire transfer';
CREATE INDEX IF NOT EXISTS prepare_iteration_index
ON prewire
(finished);
COMMENT ON INDEX prepare_iteration_index
- IS 'for wire_prepare_data_get and gc_prewire';
+ IS 'for gc_prewire';
+
+CREATE INDEX IF NOT EXISTS prepare_get_index
+ ON prewire
+ (failed,finished);
+COMMENT ON INDEX prepare_get_index
+ IS 'for wire_prepare_data_get';
+
+
+CREATE TABLE IF NOT EXISTS wire_accounts
+ (payto_uri VARCHAR PRIMARY KEY
+ ,master_sig BYTEA CHECK (LENGTH(master_sig)=64)
+ ,is_active BOOLEAN NOT NULL
+ ,last_change INT8 NOT NULL
+ );
+COMMENT ON TABLE wire_accounts
+ IS 'Table with current and historic bank accounts of the exchange. Entries never expire as we need to remember the last_change column indefinitely.';
+COMMENT ON COLUMN wire_accounts.payto_uri
+ IS 'payto URI (RFC 8905) with the bank account of the exchange.';
+COMMENT ON COLUMN wire_accounts.master_sig
+ IS 'Signature of purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS';
+COMMENT ON COLUMN wire_accounts.is_active
+ IS 'true if we are currently supporting the use of this account.';
+COMMENT ON COLUMN wire_accounts.last_change
+ IS 'Latest time when active status changed. Used to detect replays of old messages.';
+-- "wire_accounts" has no BIGSERIAL because it is a 'mutable' table
+-- and is of no concern to the auditor
+
+
+CREATE TABLE IF NOT EXISTS work_shards
+ (shard_serial_id BIGSERIAL UNIQUE
+ ,last_attempt INT8 NOT NULL
+ ,start_row INT8 NOT NULL
+ ,end_row INT8 NOT NULL
+ ,completed BOOLEAN NOT NULL DEFAULT FALSE
+ ,job_name VARCHAR NOT NULL
+ ,PRIMARY KEY (job_name, start_row)
+ );
+COMMENT ON TABLE work_shards
+ IS 'coordinates work between multiple processes working on the same job';
+COMMENT ON COLUMN work_shards.shard_serial_id
+ IS 'unique serial number identifying the shard';
+COMMENT ON COLUMN work_shards.last_attempt
+ IS 'last time a worker attempted to work on the shard';
+COMMENT ON COLUMN work_shards.completed
+ IS 'set to TRUE once the shard is finished by a worker';
+COMMENT ON COLUMN work_shards.start_row
+ IS 'row at which the shard scope starts, inclusive';
+COMMENT ON COLUMN work_shards.end_row
+ IS 'row at which the shard scope ends, exclusive';
+COMMENT ON COLUMN work_shards.job_name
+ IS 'unique name of the job the workers on this shard are performing';
+
+CREATE INDEX IF NOT EXISTS work_shards_index
+ ON work_shards
+ (job_name
+ ,completed
+ ,last_attempt
+ );
+
+
+CREATE UNLOGGED TABLE IF NOT EXISTS revolving_work_shards
+ (shard_serial_id BIGSERIAL UNIQUE
+ ,last_attempt INT8 NOT NULL
+ ,start_row INT4 NOT NULL
+ ,end_row INT4 NOT NULL
+ ,active BOOLEAN NOT NULL DEFAULT FALSE
+ ,job_name VARCHAR NOT NULL
+ ,PRIMARY KEY (job_name, start_row)
+ );
+COMMENT ON TABLE revolving_work_shards
+ IS 'coordinates work between multiple processes working on the same job with partitions that need to be repeatedly processed; unlogged because on system crashes the locks represented by this table will have to be cleared anyway, typically using "taler-exchange-dbinit -s"';
+COMMENT ON COLUMN revolving_work_shards.shard_serial_id
+ IS 'unique serial number identifying the shard';
+COMMENT ON COLUMN revolving_work_shards.last_attempt
+ IS 'last time a worker attempted to work on the shard';
+COMMENT ON COLUMN revolving_work_shards.active
+ IS 'set to TRUE when a worker is active on the shard';
+COMMENT ON COLUMN revolving_work_shards.start_row
+ IS 'row at which the shard scope starts, inclusive';
+COMMENT ON COLUMN revolving_work_shards.end_row
+ IS 'row at which the shard scope ends, exclusive';
+COMMENT ON COLUMN revolving_work_shards.job_name
+ IS 'unique name of the job the workers on this shard are performing';
+
+CREATE INDEX IF NOT EXISTS revolving_work_shards_index
+ ON revolving_work_shards
+ (job_name
+ ,active
+ ,last_attempt
+ );
+
+
-- Complete transaction
diff --git a/src/exchangedb/exchange-0002.sql b/src/exchangedb/exchange-0002.sql
deleted file mode 100644
index 175ffb39..00000000
--- a/src/exchangedb/exchange-0002.sql
+++ /dev/null
@@ -1,473 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2020-2021 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY