Compare commits

..

213 Commits

Author SHA1 Message Date
8706a36c95
-first steps towards testing age-withdraw 2023-07-18 13:34:44 +02:00
2eb3ff1f64
[age-withdraw] simplify lib-API 2023-07-18 13:34:27 +02:00
Christian Grothoff
2ca7ce1b89
bump man pages 2023-07-16 16:01:37 +02:00
Christian Grothoff
1e5976a3e9
-remove linger 2023-07-16 15:44:40 +02:00
Christian Grothoff
0698835fed
introduce TALER_TESTING_cmd_exec_wirewatch2 2023-07-16 14:36:20 +02:00
0a4422f5fa
-codespell fix 2023-07-16 13:35:03 +02:00
77f99b6363
-doxygen fixes 2023-07-16 13:33:08 +02:00
Christian Grothoff
3ce29a711b
clean up taler-bank-benchmark 2023-07-16 12:09:23 +02:00
Christian Grothoff
ec03f262b6
fix taler-aggregator-benchmark 2023-07-16 11:25:22 +02:00
Christian Grothoff
942dd73794
-ignore 2023-07-15 22:37:21 +02:00
Christian Grothoff
cd5fafffe2
-get taler-exchange-benchmark to work 2023-07-15 22:33:49 +02:00
Christian Grothoff
5b14fd547e
fix crash with -a option 2023-07-15 19:58:55 +02:00
Christian Grothoff
cd45ba408a
-fix up config 2023-07-15 19:29:13 +02:00
Christian Grothoff
ea47fb40c0
include taler-unified-setup in Debian package 2023-07-15 19:13:48 +02:00
Christian Grothoff
22ad41a2b4
-update man pages 2023-07-15 19:11:24 +02:00
Christian Grothoff
3976729af1
-update man pages 2023-07-15 19:08:19 +02:00
c86a169fb5
-typo fix fix 2023-07-15 18:20:48 +02:00
3bebbfc2da
-typo fix 2023-07-15 18:19:12 +02:00
7837015692
Merge branch 'master' into age-withdraw 2023-07-15 18:15:23 +02:00
a3922302c7
[age-withdraw] added TALER_EXCHANGE_age_withdraw_blind API 2023-07-15 18:15:07 +02:00
Christian Grothoff
d3607c286d
clean up taler-exchange-benchmark code 2023-07-15 16:08:06 +02:00
Christian Grothoff
49f8332ef9
-doxygen 2023-07-15 13:13:36 +02:00
ecea165db7
[age-withdraw] age-withdraw-reveal lib-API mostly finished 2023-07-15 09:39:01 +02:00
63efa1f135
Merge branch 'age-withdraw' of ssh://git.kesim.org/taler/exchange into age-withdraw 2023-07-14 10:02:01 +02:00
34f44ccb27
Merge branch 'master' into age-withdraw 2023-07-14 09:25:43 +02:00
Christian Grothoff
de24415e17
-avoid deep recursion issues 2023-07-14 05:27:14 +02:00
Christian Grothoff
e08fe4eff8
bump protocol version to 16 2023-07-13 23:11:10 +02:00
Christian Grothoff
b60b339ee4
merging /keys and /wire 2023-07-13 23:07:33 +02:00
65c861027a
-towards reveal response handling in agew-withdraw-reveal 2023-07-13 22:28:34 +02:00
c8250cae25
-initial file for testing age-withdraw, not functional yet 2023-07-13 08:45:39 +02:00
fde760aef0
towards age-withdraw and -reveal API 2023-07-13 08:45:05 +02:00
Christian Grothoff
a5451527cb
implement 'lost' field for #7883 2023-07-11 20:36:52 +02:00
Christian Grothoff
e984dbd8f4
merge /wire into /keys response 2023-07-11 20:01:44 +02:00
e0f78bc3c9
-update doc/prebuilt 2023-07-11 09:59:07 +02:00
4ebdcc0247
-gana update 2023-07-11 09:37:32 +02:00
8a7bfefa38
-handle some of the codespell complaints 2023-07-11 09:21:34 +02:00
88ecba945e
-handle doxygen complaints 2023-07-11 09:06:28 +02:00
Christian Grothoff
43d5e5707c
towards using taler-unified-setup in auditor tests 2023-07-10 23:20:08 +02:00
Christian Grothoff
1e572ebcab
-typos 2023-07-10 19:48:24 +02:00
Christian Grothoff
c6676f1aa5
-fix indent 2023-07-10 17:58:03 +02:00
Christian Grothoff
6c44755458
use DK age group, not global age group 2023-07-10 16:55:26 +02:00
Christian Grothoff
6a483b51ec
fix alignment issue, ensure we hash over packed structure to avoid non-determinism 2023-07-10 16:34:01 +02:00
Christian Grothoff
66f9a5b5e5
-simplify 2023-07-10 15:26:53 +02:00
Christian Grothoff
f4abc1c369
tip -> reward 2023-07-10 10:34:33 +02:00
Christian Grothoff
c57c3463d1
Tip => Reward 2023-07-10 10:34:33 +02:00
8d6b0944da
gana update 2023-07-10 10:29:11 +02:00
6107e99559
Merge branch 'master' of ssh://git.taler.net/exchange 2023-07-10 10:25:29 +02:00
3024dc9fa5
fix memory leaks reported by valgrind 2023-07-10 10:23:52 +02:00
9d706a01a2
added expected HTTP-code to logger 2023-07-10 10:22:29 +02:00
Christian Grothoff
c05f832048
-current use is good 2023-07-06 12:40:18 +02:00
Christian Grothoff
1026a32c71
-no return is correct here 2023-07-06 12:39:38 +02:00
7e9f5324b7
-loglevel INFO 2023-07-06 11:30:45 +02:00
Christian Grothoff
c02d88c8e3
work on keys serialization/deserialization 2023-07-06 00:08:00 +02:00
Christian Grothoff
11ea6fcfce
fix mime-type matching (#7882) 2023-07-05 18:20:58 +02:00
Christian Grothoff
f2c3443860
fix mime-type matching (#7882) 2023-07-05 18:20:46 +02:00
Christian Grothoff
95c05a8827
fix mime-type matching (#7882) 2023-07-05 18:19:54 +02:00
Christian Grothoff
40dfb94e0f
fix mime-type matching (#7882) 2023-07-05 18:18:23 +02:00
Christian Grothoff
1db17d43bd
-misc minor fixes 2023-07-05 13:13:16 +02:00
Christian Grothoff
999db0fb80
-bugfixes 2023-07-04 23:21:47 +02:00
Christian Grothoff
ff8349e6e7
more exchange API refactoring 2023-07-04 15:37:46 +02:00
47620fa81b
better error handling; use handle result 2023-07-04 11:57:27 +02:00
Christian Grothoff
d3772a834f
more API clean up 2023-07-03 18:39:06 +02:00
Christian Grothoff
290268e9af
more work on API atomization 2023-07-03 17:51:13 +02:00
Christian Grothoff
75ea35722b
more work on API atomization 2023-07-03 17:22:33 +02:00
f969bd3c5b
Merge branch 'master' into age-withdraw 2023-07-03 16:20:44 +02:00
40629e8992
[age-withdraw] added library function for age-withdraw
- Added TALER_EXCHANGE_age_withdraw
- Also: Change TALER_EXCHANGE_batch_withdraw and related functions to
  use GNUNET_CURL_ctx, TALER_EXCHANGE_keys and const char *echange_url
2023-07-03 16:18:40 +02:00
Christian Grothoff
2d4ebd3fc3
unconfuse pay and deposit 2023-07-02 16:09:09 +02:00
Christian Grothoff
8d6bce26ad
clarify what happens on block 2023-07-02 15:22:28 +02:00
Christian Grothoff
2d5f0a87e0
fix build 2023-07-02 14:57:48 +02:00
Christian Grothoff
c6e3cba61d
edits 2023-07-02 14:52:36 +02:00
Christian Grothoff
a8b3f0eb4e
work on regulator text 2023-07-02 14:48:27 +02:00
Christian Grothoff
e961b29103
Merge branch 'master' of git+ssh://git.taler.net/exchange 2023-07-02 14:34:45 +02:00
Christian Grothoff
67262173a1
forgot to add KYB file 2023-07-02 14:34:41 +02:00
4d8d6d1222
Added \newcommand for \LAND, \TALER, \CURRENCY; inserted package for enumeration; file renders to PDF now
Signed-off-by: Stefan Kügel <skuegel@web.de>
2023-07-02 14:18:34 +02:00
Christian Grothoff
78ed6228eb
migrate Stefan's translation back into the public git 2023-07-02 11:39:54 +02:00
Christian Grothoff
57527a5e8d
dce 2023-07-01 22:06:47 +02:00
9c3ddcbc18
added TALER_adult_age(struct TALER_AgeMask *mask) 2023-06-27 18:57:05 +02:00
537206e49f
-update author 2023-06-27 18:56:27 +02:00
e889179bdd
[auditor] use the date for the report-directory 2023-06-27 13:09:16 +02:00
d3d744a444
[testing] start an oauth2 server which returns static birthdates 2023-06-27 12:41:22 +02:00
1be14a3416
[age-withdraw] added handlers for age-withdraw to router 2023-06-27 09:48:51 +02:00
2f21fa24e4
-rename variable 2023-06-27 09:48:07 +02:00
89de1678ef
-fix typo 2023-06-27 09:47:33 +02:00
f8536e8c14
-fix parameter in stored procedures 2023-06-27 09:46:31 +02:00
145310e20e
added birthdate parser 2023-06-27 09:45:50 +02:00
ddd0e0af13
-fixed typos 2023-06-26 15:04:57 +02:00
ee42b70692
add exchange_do_age_withdraw.sql 2023-06-26 11:47:15 +02:00
eeece1c96d
-ruuid not needed 2023-06-26 11:46:13 +02:00
17001e445d
-free spec 2023-06-26 09:44:33 +02:00
c5c3a44c25
-comment updates 2023-06-26 09:44:22 +02:00
b87d1112ea
Merge branch 'master' into age-withdraw-merge 2023-06-26 00:09:01 +02:00
ddedf03a81
[age-withdraw] age-withdraw commit- and reveal-handlers implemented, 12/n
The handlers for the commit- and reveal-phases of the age-withdraw
HTTP-endpoints are implemented, yet not active.

Still missing:

- support for age-withdraw is missing in lib/.
- tests
2023-06-26 00:01:31 +02:00
Christian Grothoff
421129a32e
next round of exchange API atomization 2023-06-25 19:10:26 +02:00
Christian Grothoff
75733ee00e
more work on atomizing exchange API: deposit 2023-06-25 13:59:47 +02:00
Christian Grothoff
e2f44ea7b3
fix test 2023-06-25 00:08:49 +02:00
Christian Grothoff
fcd3948f3b
-fix typo 2023-06-25 00:06:35 +02:00
Christian Grothoff
d25dc8b0ad
fix #7870 2023-06-23 11:41:52 +02:00
Christian Grothoff
720783b66a
-more exchange API atomization 2023-06-22 22:05:34 +02:00
Christian Grothoff
999dae7c5d
-more exchange API atomization 2023-06-22 20:42:10 +02:00
Christian Grothoff
d4a65faad4
-more exchange API atomization 2023-06-22 20:37:15 +02:00
Christian Grothoff
7bb9547599
more API cleanup 2023-06-22 20:26:34 +02:00
Christian Grothoff
22d5b9fc3a
-fail, not skip 2023-06-22 16:31:51 +02:00
Christian Grothoff
32d5b90827
fix typo 2023-06-22 11:03:36 +02:00
Christian Grothoff
ee2471a8c3
fix uninitialized SANDBOX_PORT if only starting sandbox without nexus 2023-06-22 10:27:11 +02:00
Christian Grothoff
ef6496aba5
new -W option for unified setup 2023-06-21 23:13:57 +02:00
Christian Grothoff
c512c8b101
-simplify 2023-06-21 09:03:55 +02:00
Christian Grothoff
f5ce22ddf6
-more clean up of auditor api: atomization complete 2023-06-21 09:00:58 +02:00
Christian Grothoff
af77a2a178
-more auditor API atomization 2023-06-21 08:15:06 +02:00
Christian Grothoff
a37a8d34d5
-towards API atomization 2023-06-21 08:02:36 +02:00
Christian Grothoff
17789253e9
ensure forward-compatibility for auditor C API 2023-06-21 07:53:17 +02:00
Christian Grothoff
d6838ed841
-doxygen fixes 2023-06-20 21:57:13 +02:00
Christian Grothoff
35bf856fcb
Merge branch 'master' of git+ssh://git.taler.net/exchange 2023-06-12 18:08:15 +02:00
Christian Grothoff
8be960125f
-reg text work 2023-06-12 18:08:08 +02:00
MS
9b20c5047e
test_bank_api_with_nexus
Fixing the wiring of accounts in the test preparation.
In particular, the name and IBAN of one debited account
(along the /admin/add-incoming) was wrongly registered
at Sandbox.
2023-06-12 16:34:35 +02:00
70bfe0ed1b
Merge branch 'master' into age-withdraw 2023-06-10 11:07:06 +02:00
Christian Grothoff
d8f8c550bd
-fix FTBFS 2023-06-09 17:52:16 +02:00
Christian Grothoff
a2dde02b64
major libtalertesting API refactoring, including no longer having taler-specific logic in the test engine core 2023-06-07 23:11:05 +02:00
Christian Grothoff
015b08b048
new command to use taler-benchmark-setup.sh 2023-06-05 17:01:23 +02:00
46188ae07e
Merge branch 'master' into age-withdraw 2023-06-05 16:39:43 +02:00
Christian Grothoff
96cbda85e3
perms 2023-06-05 15:58:52 +02:00
Christian Grothoff
4355a08769
-docu 2023-06-05 15:58:03 +02:00
Christian Grothoff
4a86d411de
expand taler-benchmark-setup script 2023-06-05 15:54:20 +02:00
Christian Grothoff
27f22ef1d2
more fixes for taler-benchmark-setup 2023-06-05 15:13:41 +02:00
98b51edf49
Merge branch 'master' into age-withdraw 2023-06-05 09:18:35 +02:00
Christian Grothoff
8cc1edfe0a
work on cleaning up benchmark logic 2023-06-05 00:24:01 +02:00
Christian Grothoff
82fd1a1164
try to create universal benchmark setup script 2023-06-04 22:33:23 +02:00
Christian Grothoff
9e7d3f9065
address FIXMEs in bank service API 2023-06-04 14:29:15 +02:00
Christian Grothoff
0ad3de938e
address API stability FIXMEs in taler_exchange_service 2023-06-04 14:10:54 +02:00
Christian Grothoff
809300158c
code cleanup, fixing misc. memory leaks in the process 2023-06-04 13:26:00 +02:00
Christian Grothoff
9718bc4920
-allow NULL for i18n 2023-06-04 01:29:21 +02:00
Christian Grothoff
8f2c2766a4
-logging 2023-06-03 13:01:12 +02:00
Christian Grothoff
f8fd492e9e
add DB idempotency test 2023-06-03 12:53:45 +02:00
fb5bc18c58
Need libgnunetpq v4.0.0++ 2023-06-03 10:46:08 +02:00
80a1b8f524
Merge branch 'master' into age-withdraw 2023-06-03 10:45:31 +02:00
2ea3ae1008
make CREATE TYPE usage idempotent
PostgresSQL doesn't have a CREATE TYPE OR REPLACE, so we have to work
around this with exception handling.
2023-06-02 11:46:48 +02:00
Christian Grothoff
04885a289a
-bump gana 2023-05-28 00:04:13 +02:00
Christian Grothoff
be40886515
fix FTBFS 2023-05-26 17:16:48 +02:00
Christian Grothoff
ae6e62a0a3
add document download logic for kycaid 2023-05-23 15:44:23 +02:00
Christian Grothoff
89e2a02380
-style 2023-05-21 19:25:31 +02:00
Christian Grothoff
d36f1b3b6a
-fix SQL 2023-05-21 19:20:09 +02:00
Christian Grothoff
aa8d44aab3
-typo 2023-05-21 18:47:39 +02:00
Christian Grothoff
721cd047ff
Merge branch 'master' of git+ssh://git.taler.net/exchange 2023-05-21 18:26:02 +02:00
Christian Grothoff
144b3a50a9
-minor fixes 2023-05-21 14:50:50 +02:00
Martin Schanzenbach
689fd46a60
Add a packaging folder to contrib and place
systemd/etc files from debian folder.
Also adds the packaging folder to the tarball.
2023-05-18 22:15:29 +02:00
Christian Grothoff
bac7123763
array-based do_reserves_in_insert 2023-05-18 14:45:28 +02:00
Christian Grothoff
9f081d28d7
-update doc/prebuilt 2023-05-18 08:48:50 +02:00
Christian Grothoff
0045eea277
-typo 2023-05-18 08:43:20 +02:00
Christian Grothoff
6cc3846f4d
use external helper for conversion also for KYCAID 2023-05-18 08:31:08 +02:00
Christian Grothoff
b30952ed72
-doxygen 2023-05-17 20:23:35 +02:00
Christian Grothoff
79671bba66
-no +1 2023-05-17 15:34:06 +02:00
Christian Grothoff
f7dc35e59e
implement logic to conditionally trigger AML review based on KYC attributes 2023-05-17 00:28:41 +02:00
Christian Grothoff
755955de28
towards KYC AML triggers 2023-05-16 22:26:39 +02:00
Christian Grothoff
e371d76cfe
add KYC_AML_TRIGGER option (not implemented) 2023-05-16 14:17:33 +02:00
Christian Grothoff
30b953ff0f
add new trigger option 2023-05-16 13:37:09 +02:00
Christian Grothoff
29694be4b1
expand configuration options in Debian package 2023-05-16 13:25:53 +02:00
4a31a180a4
[æge-withdraw] WiP: towards new API 2023-05-14 13:11:36 +02:00
Christian Grothoff
ef2059c9df
allow '#' 2023-05-14 12:12:49 +02:00
Christian Grothoff
4e5a9906c2
more AML_THRESHOLD fixes 2023-05-13 22:08:45 +02:00
Christian Grothoff
89ed38c03c
typos 2023-05-13 22:04:14 +02:00
Christian Grothoff
40e45e7f5e
avoid generating useless file by tests 2023-05-13 22:02:10 +02:00
Christian Grothoff
bac4932cf5
-fix templating tests 2023-05-13 22:00:04 +02:00
Christian Grothoff
b7d1ca4cd9
strcasecmp 2023-05-13 20:37:58 +02:00
Christian Grothoff
89e21002d6
return tipping_allowed from /keys 2023-05-13 20:31:47 +02:00
Christian Grothoff
90ca90b576
-start with new ENABLE_TIPPING option 2023-05-13 20:23:38 +02:00
Christian Grothoff
92df1cb1da
-move AML_THRESHOLD option to exchange 2023-05-13 20:22:16 +02:00
Christian Grothoff
c9ed524bc3
add logic to oauth2 plugin to use /setup endpoint when configured 2023-05-13 17:06:21 +02:00
9f1f069cea
Merge branch 'age-withdraw' of ssh://git.kesim.org/taler/exchange into age-withdraw 2023-05-12 15:10:59 +02:00
a04425df34
[age-withdraw] WIP: change schema to use new support for array types, 11/n 2023-05-12 15:10:23 +02:00
4833234df6
Merge branch 'master' into age-withdraw 2023-05-12 13:40:22 +02:00
Christian Grothoff
ff1a28319f
-fix FTBFS 2023-05-11 15:24:16 +02:00
Christian Grothoff
bc03a27cba
-fix doxygen 2023-05-11 11:47:35 +02:00
Christian Grothoff
7899bc5621
externalize persona JSON conversion logic, expand with file download 2023-05-11 01:18:24 +02:00
Christian Grothoff
ec8ad2e3b3
update mustach library 2023-05-11 01:16:53 +02:00
Christian Grothoff
0dd0fff17d
-typo 2023-05-10 22:13:18 +02:00
Christian Grothoff
b15713f42e
add helper logic for JSON2JSON conversion 2023-05-10 22:09:47 +02:00
Christian Grothoff
4954963405
ensure amounts are in upper-case 2023-05-10 09:13:09 +02:00
Christian Grothoff
5259ea0532
-fix logging 2023-05-10 01:21:52 +02:00
Christian Grothoff
cc34502ac1
-fix TOTP calculation 2023-05-10 01:13:45 +02:00
Christian Grothoff
5f9c3021db
exceptions-apply 2023-05-10 01:08:05 +02:00
Christian Grothoff
09f09a2104
fix more VLAs 2023-05-10 01:07:20 +02:00
Christian Grothoff
c014acf3c4
always use GNUNET_memcpy 2023-05-10 01:01:37 +02:00
Christian Grothoff
3ebd0a70b2
fix 0-length VLAs 2023-05-10 00:57:04 +02:00
Christian Grothoff
aedd13a778
assertions to make compiler happy 2023-05-10 00:27:33 +02:00
Christian Grothoff
1cf58e8ff8
-fix warning 2023-05-10 00:02:52 +02:00
Christian Grothoff
4e79967f9b
-consistency 2023-05-09 23:53:02 +02:00
dc5b0fb0d3
Fix age mask parsing from config
- initialize age mask to zero
- drop default bitstring for age mask, use string instead

-remove default age mask bits, use string instead

-strdup
2023-05-09 21:45:59 +02:00
Christian Grothoff
d1379e492d
-fix include 2023-05-09 14:11:21 +02:00
Christian Grothoff
f009e0bd12
-make content-length optional again 2023-05-09 14:07:13 +02:00
Christian Grothoff
85f6c8cdcc
add kyc collection 2023-05-07 21:41:54 +02:00
f40932196e
flows: minor edits 2023-05-07 18:37:24 +02:00
Christian Grothoff
fddd06c152
proc doc 2023-05-07 17:52:54 +02:00
Christian Grothoff
6d363488a1
allow NULL 2023-05-07 00:16:06 +02:00
Christian Grothoff
1639cefa61
-doxygen 2023-05-06 21:29:43 +02:00
Christian Grothoff
404b2b78f1
add convenience function TALER_TEMPLATING_reply_error 2023-05-06 20:55:40 +02:00
Christian Grothoff
1f9427e1d9
add convenience function for content-length limiation 2023-05-06 19:43:17 +02:00
Christian Grothoff
737b3338ed
-doxygen 2023-05-04 21:32:36 +02:00
Christian Grothoff
1e88796045
finish implementation for #7808 2023-05-04 17:36:43 +02:00
Christian Grothoff
7c0de44a2b
towards LP support for GET /deposits (#7808) 2023-05-04 17:14:54 +02:00
Christian Grothoff
2de2b6e3cf
-fix crypto test 2023-05-04 16:04:29 +02:00
Christian Grothoff
4c1a2c0307
more shared logic for argument/header parsing 2023-05-04 14:42:06 +02:00
Christian Grothoff
0b8752bb1b
-more readable, no semantic change 2023-05-03 20:25:39 +02:00
Christian Grothoff
82bb911720
-update gana 2023-05-02 23:23:47 +02:00
Christian Grothoff
8e0f9b40c0
check regex syntax at least 2023-05-02 21:48:23 +02:00
Christian Grothoff
4267f1d762
include 0-terminator when hashing 2023-05-02 17:32:26 +02:00
Christian Grothoff
ffd4057c61
use same canonicalization of JSON as for contract hashes when doing normal JSON hashing 2023-05-02 17:29:41 +02:00
Christian Grothoff
41cb79c685
implement taler-exchange-offline account restriction and conversion functionality 2023-05-02 12:07:27 +02:00
Christian Grothoff
00021d7e83
-doxygen 2023-05-02 11:03:26 +02:00
Christian Grothoff
8ce9433736
fix uninitialized old_scope 2023-05-01 23:10:44 +02:00
Christian Grothoff
647ae694cc
-fix uninitialized variable and memory leak 2023-05-01 22:34:35 +02:00
Christian Grothoff
faca037018
expose TALER_EXCHANGE_parse_accounts() in external API 2023-05-01 22:34:35 +02:00
Christian Grothoff
75f75c4a51
breaking protocol changes towards fixing #7810 (incomplete, taler-exchange-offline still unfinished) 2023-05-01 22:34:35 +02:00
9130cda9e7
Merge branch 'master' into age-withdraw 2023-04-23 11:57:15 +02:00
3ec14744f0
-fix typos for doxygen 2023-04-23 11:10:06 +02:00
469 changed files with 29826 additions and 21626 deletions

View File

@ -274,11 +274,11 @@ AS_CASE([$with_gnunet],
CPPFLAGS="-I$with_gnunet/include ${CPPFLAGS}"])
CPPFLAGS="${CPPFLAGS} ${POSTGRESQL_CPPFLAGS}"
AC_CHECK_HEADERS([gnunet/gnunet_pq_lib.h],
[AC_CHECK_LIB([gnunetpq], [GNUNET_PQ_result_spec_string], libgnunetpq=1)])
[AC_CHECK_LIB([gnunetpq], [GNUNET_PQ_result_spec_array_string], libgnunetpq=1)])
AS_IF([test $libgnunetpq != 1],
[AC_MSG_ERROR([[
***
*** You need libgnunetpq to build this program.
*** You need libgnunetpq version >= 4.0.0 to build this program.
*** Make sure you have Postgres installed while
*** building GNUnet (and that your GNUnet version
*** is recent!)

View File

@ -70,7 +70,8 @@ EXTRA_DIST = \
$(rdata_DATA) \
coverage.sh \
gnunet.tag \
microhttpd.tag
microhttpd.tag \
packages
# Change the set of supported languages here. You should
# also update tos'XX'data and EXTRA_DIST accordingly.

@ -1 +1 @@
Subproject commit bd4e73b2ed06269fdee42eaad21acb5be8be9302
Subproject commit 86b36917a59cc46961a9c9042b1af75a88545558

View File

@ -31,7 +31,13 @@
<member kind="define">
<type>#define</type>
<name>GNUNET_TIME_UNIT_FOREVER_ABS</name>
<anchorfile>gnunet_util_lib.h</anchorfile>
<anchorfile>gnunet_time_lib.h</anchorfile>
<arglist></arglist>
</member>
<member kind="define">
<type>#define</type>
<name>GNUNET_TIME_UNIT_ZERO_ABS</name>
<anchorfile>gnunet_time_lib.h</anchorfile>
<arglist></arglist>
</member>
</compound>

View File

@ -0,0 +1 @@
# This configuration will be changed by tooling. Do not touch it manually.

View File

@ -0,0 +1,49 @@
# Main entry point for the GNU Taler configuration.
#
# Structure:
# - taler.conf is the main configuration entry point
# used by all Taler components (the file you are currently
# looking at.
# - overrides.conf contains configuration overrides that are
# set by some tools that help with the configuration,
# and should not be edited by humans. Comments in this file
# are not preserved.
# - conf.d/ contains configuration files for
# Taler components, which can be read by all
# users of the system and are included by the main
# configuration.
# - secrets/ contains configuration snippets
# with secrets for particular services.
# These files should have restrictive permissions
# so that only users of the relevant services
# can read it. All files in it should end with
# ".secret.conf".
[taler]
# Currency of the Taler deployment. This setting applies to all Taler
# components that only support a single currency.
#currency = KUDOS
# Smallest currency unit handled by the underlying bank system. Taler payments
# can make payments smaller than this units, but interactions with external
# systems is always rounded to this unit.
#currency_round_unit = KUDOS:0.01
# Monthly amount that mandatorily triggers an AML check
#AML_THRESHOLD = KUDOS:10000000
[paths]
TALER_HOME = /var/lib/taler
TALER_RUNTIME_DIR = /run/taler
TALER_CACHE_HOME = /var/cache/taler
TALER_CONFIG_HOME = /etc/taler
TALER_DATA_HOME = /var/lib/taler
# Inline configurations from all Taler components.
@inline-matching@ conf.d/*.conf
# Overrides from tools that help with configuration.
@inline@ overrides.conf

View File

@ -0,0 +1,4 @@
<Location "/taler-auditor/">
ProxyPass "unix:/var/lib/taler-auditor/auditor.sock|http://example.com/"
RequestHeader add "X-Forwarded-Proto" "https"
</Location>

View File

@ -0,0 +1,18 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
access_log /var/log/nginx/auditor.log;
error_log /var/log/nginx/auditor.err;
location /taler-auditor/ {
proxy_pass http://unix:/var/lib/taler-auditor/auditor.sock;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host "localhost";
#proxy_set_header X-Forwarded-Proto "https";
}
}

View File

@ -0,0 +1,12 @@
# Read secret sections into configuration, but only
# if we have permission to do so.
@inline-secret@ auditordb-postgres ../secrets/auditor-db.secret.conf
[auditor]
# Debian package is configured to use a reverse proxy with a UNIX
# domain socket. See nginx/apache configuration files.
SERVE = UNIX
UNIXPATH = /var/lib/taler-auditor/auditor.sock
# Only supported database is Postgres right now.
DATABASE = postgres

View File

@ -0,0 +1,10 @@
# Database configuration for the Taler auditor.
[auditordb-postgres]
# Typically, there should only be a single line here, of the form:
CONFIG=postgres:///DATABASE
# The details of the URI depend on where the database lives and how
# access control was configured.

View File

@ -0,0 +1,4 @@
<Location "/taler-exchange/">
ProxyPass "unix:/run/taler/exchange-httpd/exchange-http.sock|http://example.com/"
RequestHeader add "X-Forwarded-Proto" "https"
</Location>

View File

@ -0,0 +1,17 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
access_log /var/log/nginx/exchange.log;
error_log /var/log/nginx/exchange.err;
location /taler-exchange/ {
proxy_pass http://unix:/run/taler/exchange-httpd/exchange-http.sock:/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host "localhost";
#proxy_set_header X-Forwarded-Proto "https";
}
}

View File

@ -0,0 +1,50 @@
# Configuration for business-level aspects of the exchange.
[exchange]
# Here you MUST add the master public key of the offline system
# which you can get using `taler-exchange-offline setup`.
# This is just an example, your key will be different!
# MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG
# MASTER_PUBLIC_KEY =
# Publicly visible base URL of the exchange.
# BASE_URL = https://example.com/
# BASE_URL =
# Here you MUST configure the amount above which transactions are
# always subject to manual AML review.
# AML_THRESHOLD =
# Attribute encryption key for storing attributes encrypted
# in the database. Should be a high-entropy nonce.
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
# For your terms of service and privacy policy, you should specify
# an Etag that must be updated whenever there are significant
# changes to either document. The format is up to you, what matters
# is that the value is updated and never re-used. See the HTTP
# specification on Etags.
# TERMS_ETAG =
# PRIVACY_ETAG =
SERVE = unix
UNIXPATH_MODE = 666
# Bank accounts used by the exchange should be specified here:
[exchange-account-1]
ENABLE_CREDIT = NO
ENABLE_DEBIT = NO
# Account identifier in the form of an RFC-8905 payto:// URI.
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
# Make sure to URL-encode spaces in $NAME!
PAYTO_URI =
# Credentials to access the account are in a separate
# config file with restricted permissions.
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf

View File

@ -0,0 +1,33 @@
#
# This configuration file specifies the various denominations offered by your
# exchange.
#
# Each denomination must be specified in a sections starting with
# "coin_".
#
# What follows is an example.
#
# [coin_FOO]
## Actual value of the coin
#VALUE = KUDOS:1
## How long will one key be used for withdrawals?
#DURATION_WITHDRAW = 7 days
## How long do users have to spend their coins?
#DURATION_SPEND = 2 years
## How long does the exchange keep the proofs around for legal disputes?
#DURATION_LEGAL = 6 years
## Fees charged. Note that for the lowest denomination, the
## fee must precisely be the lowest denomination, or zero.
#FEE_WITHDRAW = KUDOS:0
#FEE_DEPOSIT = KUDOS:0
#FEE_REFRESH = KUDOS:0
#FEE_REFUND = KUDOS:0
## How long should the RSA keys be. Do not change unless you really know
## what you are doing (consult your local cryptographer first!).
#RSA_KEYSIZE = 2048

View File

@ -0,0 +1,13 @@
# Configuration settings for system parameters of the exchange.
# Read secret sections into configuration, but only
# if we have permission to do so.
@inline-secret@ exchangedb-postgres ../secrets/exchange-db.secret.conf
[exchange]
# Only supported database is Postgres right now.
DATABASE = postgres

View File

@ -0,0 +1,17 @@
# This file contains the secret credentials
# to access the Taler Wire Gateway API (usually
# provided by LibEuFin) for the exchange accounts.
#
# Each exchange-account-* section should have a matching
# exchange-accountcredentials-* section here.
#
# Each of those sections must be imported via @inline-secret@,
# usually in conf.d/exchange-business.conf.
[exchange-accountcredentials-1]
wire_gateway_auth_method = basic
password =
username =
wire_gateway_url =

View File

@ -0,0 +1,10 @@
# Database configuration for the Taler exchange.
[exchangedb-postgres]
# Typically, there should only be a single line here, of the form:
# CONFIG=postgres:///DATABASE
# The details of the URI depend on where the database lives and how
# access control was configured.

View File

@ -0,0 +1,12 @@
[Unit]
Description=GNU Taler payment system auditor REST API
After=postgres.service network.target
[Service]
User=taler-auditor-httpd
Type=simple
Restart=on-failure
ExecStart=/usr/bin/taler-auditor-httpd -c /etc/taler/taler.conf
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange aggregator service
PartOf=taler-exchange.target
After=postgres.service
[Service]
User=taler-exchange-aggregator
Type=simple
Restart=always
RestartSec=1s
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
RuntimeMaxSec=3600s

View File

@ -0,0 +1,17 @@
[Unit]
Description=GNU Taler payment system exchange aggregator service
PartOf=taler-exchange.target
[Service]
User=taler-exchange-aggregator
Type=simple
Restart=always
RestartSec=1s
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
RuntimeMaxSec=3600s

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange closer service
PartOf=taler-exchange.target
After=network.target postgres.service
[Service]
User=taler-exchange-closer
Type=simple
Restart=always
RestartSec=1s
ExecStart=/usr/bin/taler-exchange-closer -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
RuntimeMaxSec=3600s

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange expire service
PartOf=taler-exchange.target
After=postgres.service
[Service]
User=taler-exchange-expire
Type=simple
Restart=always
RestartSec=1s
ExecStart=/usr/bin/taler-exchange-expire -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
RuntimeMaxSec=3600s

View File

@ -0,0 +1,33 @@
[Unit]
Description=GNU Taler payment system exchange REST API
AssertPathExists=/run/taler/exchange-httpd
Requires=taler-exchange-httpd.socket taler-exchange-secmod-cs.service taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
After=postgres.service network.target taler-exchange-secmod-cs.service taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
PartOf=taler-exchange.target
[Service]
User=taler-exchange-httpd
Type=simple
# Depending on the configuration, the service process kills itself and then
# needs to be restarted. Thus no significant delay on restarts.
Restart=always
RestartSec=1ms
# Disable the service if more than 5 restarts are encountered within 5s.
# These are usually the systemd defaults, but can be overwritten, thus we set
# them here explicitly, as the exchange code assumes StartLimitInterval
# to be >=5s.
StartLimitBurst=5
StartLimitInterval=5s
ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=no
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,27 @@
% This is a systemd service template.
[Unit]
Description=GNU Taler payment system exchange REST API at %I
AssertPathExists=/run/taler/exchange-httpd
Requires=taler-exchange-httpd@%i.socket taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
After=postgres.service network.target taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
PartOf=taler-exchange.target
[Service]
User=taler-exchange-httpd
Type=simple
# Depending on the configuration, the service suicides and then
# needs to be restarted.
Restart=always
# Do not dally on restarts.
RestartSec=1ms
EnvironmentFile=/etc/environment
ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=no
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange CS security module
AssertPathExists=/run/taler/exchange-secmod-cs
PartOf=taler-exchange.target
[Service]
User=taler-exchange-secmod-cs
Type=simple
Restart=always
RestartSec=100ms
ExecStart=/usr/bin/taler-exchange-secmod-cs -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=no
PrivateDevices=yes
ProtectSystem=full
IPAddressDeny=any
Slice=taler-exchange.slice

View File

@ -0,0 +1,19 @@
[Unit]
Description=GNU Taler payment system exchange EdDSA security module
AssertPathExists=/run/taler/exchange-secmod-eddsa
PartOf=taler-exchange.target
[Service]
User=taler-exchange-secmod-eddsa
Type=simple
Restart=always
RestartSec=100ms
ExecStart=/usr/bin/taler-exchange-secmod-eddsa -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=no
PrivateDevices=yes
ProtectSystem=full
IPAddressDeny=any
Slice=taler-exchange.slice

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange RSA security module
AssertPathExists=/run/taler/exchange-secmod-rsa
PartOf=taler-exchange.target
[Service]
User=taler-exchange-secmod-rsa
Type=simple
Restart=always
RestartSec=100ms
ExecStart=/usr/bin/taler-exchange-secmod-rsa -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=no
PrivateDevices=yes
ProtectSystem=full
IPAddressDeny=any
Slice=taler-exchange.slice

View File

@ -0,0 +1,18 @@
[Unit]
Description=Taler Exchange Transfer Service
After=network.target postgres.service
PartOf=taler-exchange.target
[Service]
User=taler-exchange-wire
Type=simple
Restart=always
RestartSec=1s
ExecStart=/usr/bin/taler-exchange-transfer -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
RuntimeMaxSec=3600s

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange wirewatch service
After=network.target postgres.service
PartOf=taler-exchange.target
[Service]
User=taler-exchange-wire
Type=simple
Restart=always
RestartSec=1s
RuntimeMaxSec=3600s
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice

View File

@ -0,0 +1,18 @@
[Unit]
Description=GNU Taler payment system exchange wirewatch service
After=network.target
PartOf=taler-exchange.target
[Service]
User=taler-exchange-wire
Type=simple
Restart=always
RestartSec=1s
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
StandardOutput=journal
StandardError=journal
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
Slice=taler-exchange.slice
RuntimeMaxSec=3600s

View File

@ -4,7 +4,7 @@
exec 1>&2
RET=0
changed=$(git diff --cached --name-only | grep -v mustach)
changed=$(git diff --cached --name-only | grep -v mustach | grep -v templating/test./)
crustified=""
for f in $changed;
@ -28,7 +28,7 @@ done
if [ $RET = 1 ];
then
echo "Run"
echo "uncrustify --no-backup -c uncrustify.cfg ${crustified}"
echo "uncrustify --replace -c uncrustify.cfg ${crustified}"
echo "before committing."
fi
exit $RET

View File

@ -12,6 +12,14 @@
# BASE_URL = https://example.com/
# BASE_URL =
# Here you MUST configure the amount above which transactions are
# always subject to manual AML review.
# AML_THRESHOLD =
# Attribute encryption key for storing attributes encrypted
# in the database. Should be a high-entropy nonce.
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
# For your terms of service and privacy policy, you should specify
# an Etag that must be updated whenever there are significant
# changes to either document. The format is up to you, what matters
@ -26,14 +34,17 @@ UNIXPATH_MODE = 666
# Bank accounts used by the exchange should be specified here:
[exchange-account-1]
enable_credit = no
enable_debit = no
ENABLE_CREDIT = NO
ENABLE_DEBIT = NO
# Account identifier in the form of an RFC-8905 payto:// URI.
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
# Make sure to URL-encode spaces in $NAME!
payto_uri =
PAYTO_URI =
# Credentials to access the account are in a separate
# config file with restricted permissions.
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf

View File

@ -8,3 +8,6 @@
# Only supported database is Postgres right now.
DATABASE = postgres

View File

@ -1,17 +1,22 @@
# Benchmarks, only install them for the dev package.
usr/bin/taler-aggregator-benchmark
usr/bin/taler-exchange-benchmark
usr/bin/taler-fakebank-run
usr/bin/taler-bank-benchmark
usr/bin/taler-exchange-benchmark
usr/bin/taler-exchange-kyc-tester
usr/bin/taler-fakebank-run
usr/bin/taler-unified-setup.sh
# Only used in test cases. Maybe these
# shouldn't even be installed?
usr/bin/taler-nexus-prepare
usr/bin/taler-bank-manage-testing
usr/bin/taler-nexus-prepare
# Man pages
usr/share/man/man1/taler-exchange-kyc-tester*
usr/share/man/man1/taler-aggregator-benchmark*
usr/share/man/man1/taler-bank-benchmark*
usr/share/man/man1/taler-exchange-benchmark*
usr/share/man/man1/taler-unified-setup*
# Headers
@ -27,5 +32,4 @@ usr/lib/*/libtalertesting.so
usr/lib/*/libtalerfakebank.so
# Documentation
usr/share/man/man1/taler-exchange-benchmark*
usr/share/info/taler-developer-manual*

View File

@ -9,6 +9,7 @@ infoimagedir = $(infodir)/images
man_MANS = \
prebuilt/man/taler.conf.5 \
prebuilt/man/taler-config.1 \
prebuilt/man/taler-aggregator-benchmark.1 \
prebuilt/man/taler-auditor.1 \
prebuilt/man/taler-auditor-dbinit.1 \
prebuilt/man/taler-auditor-exchange.1 \
@ -16,6 +17,7 @@ man_MANS = \
prebuilt/man/taler-auditor-offline.1 \
prebuilt/man/taler-auditor-sign.1 \
prebuilt/man/taler-auditor-sync.1 \
prebuilt/man/taler-bank-benchmark.1 \
prebuilt/man/taler-bank-transfer.1 \
prebuilt/man/taler-exchange-aggregator.1 \
prebuilt/man/taler-exchange-benchmark.1 \
@ -24,6 +26,7 @@ man_MANS = \
prebuilt/man/taler-exchange-drain.1 \
prebuilt/man/taler-exchange-expire.1 \
prebuilt/man/taler-exchange-httpd.1 \
prebuilt/man/taler-exchange-kyc-aml-pep-trigger.1 \
prebuilt/man/taler-exchange-kyc-tester.1 \
prebuilt/man/taler-exchange-offline.1 \
prebuilt/man/taler-exchange-router.1\
@ -38,7 +41,8 @@ man_MANS = \
prebuilt/man/taler-helper-auditor-deposits.1\
prebuilt/man/taler-helper-auditor-purses.1\
prebuilt/man/taler-helper-auditor-reserves.1\
prebuilt/man/taler-helper-auditor-wire.1
prebuilt/man/taler-helper-auditor-wire.1 \
prebuilt/man/taler-unified-setup.1
info_TEXINFOS = \
prebuilt/texinfo/taler-auditor.texi \

File diff suppressed because it is too large Load Diff

3
doc/flows/Makefile Normal file
View File

@ -0,0 +1,3 @@
all:
pdflatex main.tex
pdflatex main.tex

39
doc/flows/fees-coins.tex Normal file
View File

@ -0,0 +1,39 @@
\section{Fees per coin} \label{sec:fees:coin}
Payments with Taler are always made using coins. Each coin has a specific
denomination, and an exchange will issue coins in different denominations (in
the same currency). The fees per coin depend on the operation and the
denomination.
The primary fee to be paid is a {\bf deposit} fee that is
charged whenever a coin is fully or partially deposited
into a bank account or another wallet.
A secondary fee to be paid is a {\bf change} fee that is
charged whenever a coin partially spent and change must
be rendered.
Coins also have an {\bf expiration} date of approximately {\bf one year}.
After the expiration date, coins become worthless. Wallets that are online
{\bf three months} {\em before} a coin expires will automatically trade any
such coins for one or more fresh coins with a later expiration date. This
process is also subject to the {\bf change} fee.
\begin{table}[h!]
\caption{Fees per coin. Coin denomination values are given in units of CHF 0.01.}
\label{table:fees:coins}
\begin{center}
\begin{tabular}{l|c|r}
{\bf Denomination} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
$2^{-4}-2^{ 0}$ & deposit & {\em CHF 0.00125} \\
$2^{-4}-2^{ 0}$ & change & {\em CHF 0.00125} \\
$2^{ 0}-2^{ 3}$ & deposit & {\em CHF 0.00250} \\
$2^{ 0}-2^{ 3}$ & change & {\em CHF 0.00125} \\
$2^{ 4}-2^{ 8}$ & deposit & {\em CHF 0.005} \\
$2^{ 4}-2^{ 8}$ & change & {\em CHF 0.00125} \\
$2^{ 8}-2^{12}$ & deposit & {\em CHF 0.01} \\
$2^{ 8}-2^{12}$ & change & {\em CHF 0.00125} \\
\end{tabular}
\end{center}
\end{table}

30
doc/flows/fees-wire.tex Normal file
View File

@ -0,0 +1,30 @@
\section{Fees per wire} \label{sec:fees:wire}
Wire fees apply whenever an exchange needs to initiate a wire transfer to
another bank account. Wire fees do not apply to every individual payment to a
merchant, as merchants can choose to {\em aggregate} multiple micropayments
into one large payment on the wire. Wire fees also do not apply to
wallet-to-wallet payments within the Taler system.
A {\bf wire} fee is applied when a merchant receives
an aggregated payment into their bank account.
A {\bf closing} fee is applied when a wallet fails to
withdraw coins and money has to be sent back to the
originating bank account.
\begin{table}[h!]
\caption{Table with wire fees. Wire fees are set annually.}
\label{table:fees:wire}
\begin{center}
\begin{tabular}{l|c|r}
{\bf Year} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
2023 & wire & {\em CHF 0.05} \\
2023 & closing & {\em CHF 0.10} \\
2024 & wire & {\em CHF 0.05} \\
2024 & closing & {\em CHF 0.10} \\
2025 & wire & {\em CHF 0.05} \\
2025 & closing & {\em CHF 0.10} \\
\end{tabular}
\end{center}
\end{table}

52
doc/flows/int-deposit.tex Normal file
View File

@ -0,0 +1,52 @@
\section{Deposit} \label{sec:deposit}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer wallet \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{bank}{\shortstack{Retail bank \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
\end{tikzpicture}
}}
\postlevel
\begin{callself}{wallet}{Review deposit fees}{}
\end{callself}
\mess[0]{wallet}{Deposit {(Coins)}}{exchange}
\begin{sdblock}{Acceptable account?}{}
\mess[0]{exchange}{{Refuse deposit}}{wallet}
\end{sdblock}
\begin{sdblock}{KYC/AML required?}{}
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
\end{callself}
\end{sdblock}
% \prelevel
% \prelevel
% \begin{sdblock}{User abort?}{}
% \mess[0]{wallet}{{Request abort}}{exchange}
% \mess[0]{exchange}{{Abort confirmation}}{wallet}
% \end{sdblock}
\mess[0]{exchange}{{Initiate transfer}}{bank}
\end{sequencediagram}
\caption{A customer deposits the coins issued by a Taler exchange (payment
service provider) into a bank account. Even if the
bank account is owned by the same customer, the
KYC checks from Section~\ref{sec:kyc:deposit} apply.}
\label{fig:int:deposit}
\end{figure}
We do {\bf not} permit the customer to regain control over their funds {\em
unless} they pass the KYC/AML checks. The technical reason is simply that
the KYC/AML checks happen {\em after} the aggregation logic and at this point
refunds are no longer permitted. From a compliance perspective, this also
prevents malicious customers from risk-free probing of the system.

60
doc/flows/int-pay.tex Normal file
View File

@ -0,0 +1,60 @@
\section{Pay} \label{sec:pay}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer wallet \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\newinst[1]{merchant}{\shortstack{Merchant \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[1]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[1]{bank}{\shortstack{Merchant bank \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] {Commercial \\ Accounts};
\end{tikzpicture}
}}
\postlevel
\mess[0]{wallet}{Browse catalog}{merchant}
\mess[0]{merchant}{Commercial offer}{wallet}
\begin{callself}{wallet}{Review offer}{}
\end{callself}
\mess[0]{wallet}{Pay {(Coins)}}{merchant}
\mess[0]{merchant}{Deposit {(Coins)}}{exchange}
\begin{sdblock}{Acceptable account?}{}
\mess[0]{exchange}{{Refuse deposit}}{merchant}
\mess[0]{merchant}{{Refund purchase}}{wallet}
\end{sdblock}
\mess[0]{exchange}{{Confirm deposit}}{merchant}
\mess[0]{merchant}{Fulfill order}{wallet}
\begin{callself}{exchange}{Aggregate transactions}{}
\end{callself}
\begin{sdblock}{KYC/AML required?}{}
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
\end{callself}
\end{sdblock}
\mess[0]{exchange}{{Initiate transfer}}{bank}
\end{sequencediagram}
\caption{Payments from a customer to merchant result in
depositing coins at the Taler exchange (payment service provider)
which then credits the merchant's bank account.
The KYC/AML checks are described in Section~\ref{sec:kyc:deposit}}
\label{fig:int:pay}
\end{figure}
{\bf Internal note:} The exchange refusing a deposit immediately based on
unaccaptable merchant accounts may not be fully implemented (this is a very
recent feature, after all); especially the merchant then automatically
refunding the purchase to the customer is certainly missing. However,
the entire situation only arises when a merchant is incorrectly configured
and in violation of the terms of service.

56
doc/flows/int-pull.tex Normal file
View File

@ -0,0 +1,56 @@
\section{Pull payment (aka invoicing)} \label{sec:pull}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{payer}{\shortstack{Payer \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{payee}{\shortstack{Payee \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\postlevel
\begin{callself}{payee}{Review pull payment fees}{}
\end{callself}
\mess[0]{payee}{{Create invoice (Wallet ID)}}{exchange}
\mess[0]{exchange}{{Invoice ready}}{payee}
\mess[0]{payee}{{Send invoice (e.g. via QR code)}}{payer}
\begin{callself}{payer}{Review invoice}{}
\end{callself}
\mess[0]{payer}{{Make payment (Coins)}}{exchange}
\begin{sdblock}{Domestic wallet?}{}
\begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
\end{callself}
\end{sdblock}
\begin{sdblock}{KYC/AML required?}{}
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
\end{callself}
\end{sdblock}
\mess[0]{exchange}{{Distribute digital cash}}{payee}
\end{sequencediagram}
\caption{Interactions between wallets and Taler exchange
in a pull payment. KYC/AML checks are described in
Section~\ref{sec:kyc:pull}.}
\label{fig:int:pull}
\end{figure}
We do {\bf not} permit the payer to regain control over their funds, once the
payment was made they are locked {\em until} the payee passes the KYC/AML
checks. We only do the AML/KYC process once the funds are locked at the
exchange. This ensures we know the actual transacted amounts (which may be
lower than the total amounts requested) and prevents risk-free probing
attacks.

48
doc/flows/int-push.tex Normal file
View File

@ -0,0 +1,48 @@
\section{Push payment} \label{sec:push}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{payer}{\shortstack{Payer \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{payee}{\shortstack{Payee \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\postlevel
\begin{callself}{payer}{Review push payment fees}{}
\end{callself}
\mess[0]{payer}{{Push funds (Coins)}}{exchange}
\mess[0]{payer}{{Offer payment (e.g. via QR code)}}{payee}
\begin{callself}{payee}{Review payment offer}{}
\end{callself}
\mess[0]{payee}{{Request funds (Wallet ID)}}{exchange}
\begin{sdblock}{Domestic wallet?}{}
\begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
\end{callself}
\end{sdblock}
\begin{sdblock}{KYC/AML required?}{}
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
\end{callself}
\end{sdblock}
\mess[0]{exchange}{{Distribute digital cash}}{payee}
% \postlevel
\begin{sdblock}{Payment offer expired?}{}
\mess[0]{exchange}{{Return funds}}{payer}
\end{sdblock}
\end{sequencediagram}
\caption{Interactions between wallets and Taler exchange
in a push payment. KYC/AML checks are described
in Section~\ref{sec:kyc:push}.}
\label{fig:int:push}
\end{figure}

39
doc/flows/int-refund.tex Normal file
View File

@ -0,0 +1,39 @@
\section{Refund}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer wallet \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\newinst[2]{merchant}{\shortstack{Merchant \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\postlevel
\begin{callself}{merchant}{Initiate refund}{}
\end{callself}
\mess[0]{merchant}{{Refund offer (QR code)}}{wallet}
\mess[0]{wallet}{Download refund}{merchant}
\mess[0]{merchant}{Approve refund}{exchange}
\mess[0]{exchange}{Confirm refund}{merchant}
\mess[0]{merchant}{Return refund confirmation}{wallet}
\end{sequencediagram}
\caption{Refund processing when a merchant is unable to fulfill
a contract. Refunds must happen {\em before} the
exchange has aggregated the original transaction for
a bank transfer to the merchant. Furthermore, refunds
can only go to the customer who made the original payment
and the refund cannot exceed the amount of the original
payment.}
\label{fig:int:refund}
\end{figure}

View File

@ -0,0 +1,48 @@
\section{Shutdown}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer wallet \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{bank}{\shortstack{Customer bank \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
\end{tikzpicture}
}}
\postlevel
\begin{callself}{exchange}{Operator initiates shutdown}{}
\end{callself}
\mess[0]{exchange}{{Shutdown alert}}{wallet}
\begin{sdblock}{Bank account known?}{}
\begin{callself}{wallet}{Designate bank account}{}
\end{callself}
\end{sdblock}
\mess[0]{wallet}{{Deposit (Coins)}}{exchange}
\begin{sdblock}{Acceptable account?}{}
\mess[0]{exchange}{{Refuse deposit}}{wallet}
\end{sdblock}
\begin{sdblock}{KYC/AML required?}{}
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
\end{callself}
\end{sdblock}
\mess[0]{exchange}{{Initiate transfer}}{bank}
\end{sequencediagram}
\caption{Shutdown interactions between customer, Taler exchange (payment
service provider) and bank.}
\label{fig:int:shutdown}
\end{figure}
KYC/AML requirements are relaxed in cases where the customer is able to
cryptographically demonstrate that they previously withdrew these coins from
the designated checking account. Thus, KYC/AML checks here primarily still
apply if the customer received the funds via P2P transfers from other wallets.

View File

@ -0,0 +1,49 @@
\section{Withdraw}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer wallet \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{bank}{\shortstack{Customer bank \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
\end{tikzpicture}
}}
\postlevel
\mess[0]{wallet}{Withdraw {(Amount)}}{exchange}
\mess[0]{exchange}{{Configuration (ToS, Fees)}}{wallet}
\begin{sdblock}{once}{}
\begin{callself}{wallet}{Accept ToS}{}
\end{callself}
\end{sdblock}
\begin{callself}{wallet}{Review withdraw fees}{}
\end{callself}
\mess[0]{wallet}{{Initiate transfer (Amount, Credit account, Wallet ID)}}{bank}
\mess[0]{bank}{{Credit (Wallet ID)}}{exchange}
\begin{sdblock}{Acceptable transfer?}{}
\mess[0]{exchange}{{Bounce funds}}{bank}
\end{sdblock}
\postlevel
\mess[0]{exchange}{Confirm wire transfer}{wallet}
\mess[0]{wallet}{Request digital cash}{exchange}
\mess[0]{exchange}{Distribute digital cash}{wallet}
\postlevel
\begin{sdblock}{Withdraw period expired?}{}
\mess[0]{exchange}{{Return remaining funds}}{bank}
\end{sdblock}
\end{sequencediagram}
\caption{Withdraw interactions between customer, Taler exchange (payment
service provider) and bank. The amount of digital cash distributed is
subject to limits per origin account (see Section~\ref{sec:kyc:withdraw}).}
\label{fig:int:withdraw}
\end{figure}

58
doc/flows/kyc-balance.tex Normal file
View File

@ -0,0 +1,58 @@
\section{KYC: Balance}
Note: this process is not implemented and would require non-trivial extra work
if required.
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
arr/.style={very thick,-latex},
every edge quotes/.style = {auto, font=\footnotesize, sloped}
]
\node (start) [start] {Start};
\node (balance) [decision,below=of start,text width=3cm] {Transaction leaves wallet balance below AML threshold?};
\node (registered) [decision,below=of balance,text width=3cm] {Wallet has been subject to KYC?};
\node (kyc) [process, below=of registered] {KYC process};
\node (aml) [process, left=of kyc] {AML process};
\node (allow) [end, right=of balance] {Allow};
\node (deny) [failed, right=of registered] {Deny};
\draw[arr] (start) -> (balance) {};
\draw[arr] (balance) -> (registered);
\draw (balance) edge["No"] (registered);
\draw[arr] (balance) -> (allow);
\draw (balance) edge["Yes"] (allow);
\draw[arr] (registered) -> (kyc);
\draw (registered) edge["No"] (kyc);
\draw[arr] (registered) -> (deny);
\draw (registered) edge["Yes"] (deny);
\draw[arr] (kyc) -> (deny);
\draw (kyc) edge["Failed"] (deny);
\draw[arr] (kyc) -> (aml);
\draw (kyc) edge["Ok"] (aml);
\draw[arr] (aml) -> (balance.west);
\draw (aml) edge["New threshold"] (balance.west);
\end{tikzpicture}
\end{center}
\caption{Regulatory process when a wallet exceeds its AML threshold.
When the transfer is denied the transaction (withdraw, P2P transfer)
is refused by the wallet.}
\end{figure}
\begin{table}[h!]
\caption{Settings for the balance trigger.}
\begin{tabular}{l|l|r}
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
KYC threshold & Amount & {\em 5000 CHF} \\
Default AML threshold & Amount & {\em 5000 CHF} \\
\end{tabular}
\end{table}

80
doc/flows/kyc-deposit.tex Normal file
View File

@ -0,0 +1,80 @@
\section{KYC: Deposit} \label{sec:kyc:deposit}
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
arr/.style={very thick,-latex},
every edge quotes/.style = {auto, font=\footnotesize, sloped}
]
\node (start) [start] {Start};
\node (country) [decision,below=of start,text width=2.5cm] {Target account in allowed country?};
\node (amount) [decision, below=of country,text width=2.5cm] {Target account received less than KYB threshold?};
\node (kyc) [process, right=of amount] {KYB process};
\node (high) [decision, below=of amount,text width=2.5cm] {Target account received more than its AML threshold?};
\node (aml) [process, right=of high] {AML process};
\node (dummy) [below right=of aml] {};
\node (allow) [end, below right=of dummy] {Allow};
\node (deny) [failed, right=of kyc] {Deny};
\draw[arr] (start) -> (country) {};
\draw[arr] (country) -> (amount);
\draw (country) edge["Yes"] (amount);
\draw[arr] (country.east) -> (deny);
\draw (country.east) edge["No"] (deny);
\draw[arr] (amount) -> (high);
\draw (amount) edge["Yes"] (high);
\draw[arr] (amount.east) -> (kyc);
\draw (amount.east) edge["No"] (kyc);
\draw[arr] (kyc) -> (deny);
\draw (kyc) edge["Failed"] (deny);
\draw[arr] (kyc) -> (high);
\draw (kyc) edge["Succeeded"] (high);
\draw[arr] (high.south) -> (allow);
\draw (high.south) edge["Yes"] (allow);
\draw[arr] (high.east) -> (aml);
\draw (high.east) edge["No"] (aml);
\draw[arr] (aml) -> (deny);
\draw (aml) edge["Violation"] (deny);
\draw[arr] (aml) -> (allow);
\draw (aml) edge["Ok"] (allow);
\end{tikzpicture}
\end{center}
\caption{Regulatory process when depositing digital cash into a bank
account. When the transfer is denied, the money is held in escrow
until authorities authorize the transfer.}
\end{figure}
\begin{table}[h!]
\caption{Settings for the deposit trigger. Note that the operation
must satisfy all of the given rules.}
\begin{tabular}{l|l|r}
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
KYB deposit threshold & Amount/month & {\em 5000 CHF} \\
KYB deposit threshold & Amount/year & {\em 25000 CHF} \\
Default AML deposit threshold & Amount/month & {\em 2500 CHF} \\
\end{tabular}
\end{table}
The KYB deposit threshold of 5'000 \CURRENCY{} per month and than 25'000
\CURRENCY{} per year ensure compliance with article 48-1b.
Additionally, our terms of service will prohibit businesses to receive
amounts exceeding 1'000 \CURRENCY{} per transaction (well below the
15'000 \CURRENCY{} threshold defined in article 24-1c).

86
doc/flows/kyc-pull.tex Normal file
View File

@ -0,0 +1,86 @@
\section{KYC/AML: Pull Payment} \label{sec:kyc:pull}
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
arr/.style={very thick,-latex},
every edge quotes/.style = {auto, font=\footnotesize, sloped}
]
\node (start) [start] {Start};
\node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
\node (domestic) [process, right=of wallet] {Validate phone number};
\node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
\node (kyc) [process, right=of amount] {KYC process};
\node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
\node (aml) [process, right=of high] {AML process};
\node (dummy) [below right=of aml] {};
\node (allow) [end, below right=of dummy] {Allow invoicing};
\node (deny) [failed, right=of kyc] {Deny};
\draw[arr] (start) -> (wallet) {};
\draw[arr] (wallet) -> (amount);
\draw (wallet) edge["Yes"] (amount);
\draw[arr] (wallet.east) -> (domestic);
\draw (wallet.east) edge["No"] (domestic);
\draw[arr] (domestic) -> (amount);
\draw (domestic) edge["Confirmed"] (amount);
\draw[arr] (domestic) -> (deny);
\draw (domestic) edge["Failed"] (deny);
\draw[arr] (amount) -> (high);
\draw (amount) edge["Yes"] (high);
\draw[arr] (amount.east) -> (kyc);
\draw (amount.east) edge["No"] (kyc);
\draw[arr] (kyc) -> (deny);
\draw (kyc) edge["Failed"] (deny);
\draw[arr] (kyc) -> (high);
\draw (kyc) edge["Succeeded"] (high);
\draw[arr] (high.south) -> (allow);
\draw (high.south) edge["Yes"] (allow);
\draw[arr] (high.east) -> (aml);
\draw (high.east) edge["No"] (aml);
\draw[arr] (aml) -> (deny);
\draw (aml) edge["Violation"] (deny);
\draw[arr] (aml) -> (allow);
\draw (aml) edge["Ok"] (allow);
\end{tikzpicture}
\end{center}
\caption{Regulatory process when receiving payments from another wallet.
The threshold depends on the risk profile from the KYC process.
When KYC thresholds would be passed, the receiving wallet cannot
generate a valid invoice until it has provided the KYC data.
When a transfer is denied by AML staff, the money is held in escrow
until authorities authorize the transfer.}
\end{figure}
\begin{table}[h!]
\caption{Settings for the pull payment trigger. Note that the operation
must satisfy all of the given rules.}
\begin{tabular}{l|l|r}
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
Permitted phone numbers & Dialing prefix & {\em +41} \\
P2P KYC threshold & Amount/month & {\em 1000 CHF} \\
P2P KYC threshold & Amount/year & {\em 5000 CHF} \\
Default P2P AML threshold & Amount/month & {\em 2500 CHF} \\
\end{tabular}
\end{table}
The P2P KYC thresholds of 1'000 \CURRENCY{} per month and than 5'000
\CURRENCY{} per year ensure compliance with article 49-2c.

84
doc/flows/kyc-push.tex Normal file
View File

@ -0,0 +1,84 @@
\section{KYC/AML: Push Payment} \label{sec:kyc:push}
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
arr/.style={very thick,-latex},
every edge quotes/.style = {auto, font=\footnotesize, sloped}
]
\node (start) [start] {Start};
\node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
\node (domestic) [process, right=of wallet] {Validate phone number};
\node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
\node (kyc) [process, right=of amount] {KYC process};
\node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
\node (aml) [process, right=of high] {AML process};
\node (dummy) [below right=of aml] {};
\node (allow) [end, below right=of dummy] {Allow};
\node (deny) [failed, right=of kyc] {Deny};
\draw[arr] (start) -> (wallet) {};
\draw[arr] (wallet) -> (amount);
\draw (wallet) edge["Yes"] (amount);
\draw[arr] (wallet.east) -> (domestic);
\draw (wallet.east) edge["No"] (domestic);
\draw[arr] (domestic) -> (amount);
\draw (domestic) edge["Confirmed"] (amount);
\draw[arr] (domestic) -> (deny);
\draw (domestic) edge["Failed"] (deny);
\draw[arr] (amount) -> (high);
\draw (amount) edge["Yes"] (high);
\draw[arr] (amount.east) -> (kyc);
\draw (amount.east) edge["No"] (kyc);
\draw[arr] (kyc) -> (deny);
\draw (kyc) edge["Failed"] (deny);
\draw[arr] (kyc) -> (high);
\draw (kyc) edge["Succeeded"] (high);
\draw[arr] (high.south) -> (allow);
\draw (high.south) edge["Yes"] (allow);
\draw[arr] (high.east) -> (aml);
\draw (high.east) edge["No"] (aml);
\draw[arr] (aml) -> (deny);
\draw (aml) edge["Violation"] (deny);
\draw[arr] (aml) -> (allow);
\draw (aml) edge["Ok"] (allow);
\end{tikzpicture}
\end{center}
\caption{Regulatory process when receiving payments from another wallet.
The threshold depends on the risk profile from the KYC process.
When the transfer is denied, the money is held in escrow
until authorities authorize the transfer.}
\end{figure}
\begin{table}[h!]
\caption{Settings for the push payment trigger. Note that the operation
must satisfy all of the given rules.}
\begin{tabular}{l|l|r}
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
Permitted phone numbers & Dialing prefix & {\em +41} \\
P2P KYC threshold & Amount/month & {\em 1000 CHF} \\
P2P KYC threshold & Amount/year & {\em 5000 CHF} \\
Default P2P AML threshold & Amount/month & {\em 2500 CHF} \\
\end{tabular}
\end{table}
The P2P KYC thresholds of 1'000 \CURRENCY{} per month and than 5'000
\CURRENCY{} per year ensure compliance with article 49-2c.

View File

@ -0,0 +1,55 @@
\section{KYC: Withdraw} \label{sec:kyc:withdraw}
\begin{figure}[h!]
\begin{center}
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
arr/.style={very thick,-latex},
every edge quotes/.style = {auto, font=\footnotesize, sloped}
]
\node (start) [start] {Start};
\node (country) [decision,below=of start,text width=3cm] {Wire transfer originates from allowed country?};
\node (amount) [decision, below=of country,text width=3cm] {Transferred less than maximum amount from origin account over last month?};
\node (allow) [end, below=of amount] {Allow};
\node (deny) [failed, right=of allow] {Deny};
\draw[arr] (start) -> (country) {};
\draw[arr] (country) -> (amount);
\draw (country) edge["Yes"] (amount);
\draw[arr] (country.east) -> (deny);
\draw (country.east) edge["No"] (deny);
\draw[arr] (amount) -> (allow);
\draw (amount) edge["Yes"] (allow);
\draw[arr] (amount.east) -> (deny);
\draw (amount.east) edge["No"] (deny);
\end{tikzpicture}
\end{center}
\caption{Regulatory process when withdrawing digital cash from a
bank account.
If the transfer is denied or the user fails to withdraw the
funds for any other reason, the money is automatically returned
after the bounce period (see Table~\ref{table:kyc:withdraw:settings}) to
the originating bank account.}
\label{fig:kyc:withdraw}
\end{figure}
\begin{table}[h!]
\caption{Settings for the withdraw trigger. Note that the operation
must satisfy all of the given rules.} \label{table:kyc:withdraw:settings}
\begin{tabular}{l|l|r}
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
SMS-Identification & Amount/month & {\em 200 CHF} \\
Withdraw limit & Amount/month & {\em 5000 CHF} \\
Withdraw limit & Amount/year & {\em 25000 CHF} \\
Bounce period & Delay & 1 month \\
\end{tabular}
\end{table}
The limit of 200 \CURRENCY{} results from article 48-2. Strictly limiting
withdrawals to less than 5'000 \CURRENCY{} per month and less than 25'000
\CURRENCY{} per year assures compliance with article 48-1c.

239
doc/flows/main.de.tex Normal file
View File

@ -0,0 +1,239 @@
% This is a (partial) translation of main.tex into
% German. Please keep the structure as parallel as
% possible when improving / expanding the translation!
\documentclass[10pt,a4paper,oneside]{book}
\usepackage[utf8]{inputenc}
\usepackage{url}
\usepackage{enumitem}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{qrcode}
\usepackage{pgf-umlsd}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usetikzlibrary{quotes}
\author{Christian Grothoff}
\title{Flows in the GNU Taler System}
\begin{document}
\tableofcontents
\newcommand\TALER{TALER OPERATIONS AG}
\newcommand\CURRENCY{CHF}
\newcommand\LAND{der Schweiz}
\section{Transaktionen im Taler-Bezahlsystem}\label{sec:Transaktionen}
Dieser Abschnitt stellt die Transaktionen im Taler-Bezahlsystem
vor. Die Grafiken geben wieder, in welcher Reihenfolge die beteiligten
Parteien interagieren. \\
F\"ur jede einzelne Transaktion ist die automatische Ausl\"osung von
Compliance-Prozessen durch den Taler-Exchange einstellbar.
Die im Rahmen des jeweiligen Compliance-Prozesses erzwungenen
Pr\"ufschritte beschreibt Abschnitt~\ref{sec:triggers}.
Folgende Transaktionen kommen als Ausl\"oser f\"ur AML- und KYC-Prozesse
in Betracht:
\begin{description}[noitemsep]
\item[withdraw] Ein Nutzer hebt digitales Bargeld (e-money) in Form von
Taler-Coins in ein Taler-Wallet ab
\item[reimburse] Ein Nutzer l\"asst den Gegenwert von Taler-Coins vom
Taler-Exchange an das urspr\"ungliche IBAN-Bankkonto zur\"uck\"uberweisen
\item[pay] Ein Nutzer zahlt zugunsten eines IBAN-Bankkontos des Empf\"angers
\item[refund] Ein Verk\"aufer erteilt einem Zahlenden die R\"uckerstattung
eines Zahlbetrags
\item[push] Ein Nutzer sendet einen Zahlbetrag an ein anderes Taler-Wallet
\item[pull] Ein Nutzer stellt einem anderen Taler-Wallet eine Rechnung aus
und fordert eine Zahlung von diesem Wallet
\item[shutdown] Der Betreiber des Taler-Exchange informiert die Inhaber von
Coins, die diese von jenem Exchange abgehoben hatten, dass der Exchange
geplant eingestellt und die Gegenwerte der Coins restituiert werden
\end{description}
Die Nutzer beginnen ein gesch\"aftliches Nutzungsverh\"altnis mit
\TALER{}, wenn sie ihre Taler-Wallets anweisen, eine Abhebung durchzuf\"uhren.
Das Taler-Bezahlsystem verwendet jedoch keine Konten, sondern wert-basierte
Token und explizit keine konten-basierten Geld-\"Aquivalente.
Taler soll digitales Bargeld sein und erlaubt technisch bedingt
kein Nachvollziehen der Transaktionen seiner Nutzer, wie es Konten mit
Eing\"angen und Ausg\"angen von Zahlungen erm\"oglichen w\"urden.
Es gibt daher kein ``Er\"offnen'' oder ``Schliessen'' von Konten der Nutzer.
Die Begriffe ``opening'' und ``closing'' lassen sich deshalb auch nicht auf
das System anwenden oder \"ubertragen. \\
Die Nutzer k\"onnen
\begin{enumerate}[noitemsep]
\item die treuh\"andisch verwalteten Einlagen gezielt auf ein bestimmtes
Bankkonto auszahlen lassen,
%(siehe Abschnitt~\ref{sec:deposit})
\item an einen Verk\"aufer zahlen,
%(siehe Abschnitt~\ref{sec:deposit})
\item einem anderen Empf\"anger mittels peer-to-peer-Verfahren Coins zukommen
lassen
%(siehe Abschnitte~\ref{sec:push} und~\ref{sec:pull})
\item die Coins in ihrem Wallet, das verloren ging oder zerst\"ort wurde,
durch Ablauf der G\"ultigkeit entwerten lassen (dies w\"are ebenso der Fall
bei einer langen Zeit ohne Internet-Anbindung oder ohne Installation),
\item den Wert der Coins im Wallet durch Zahlung von Geb\"uhren f\"ur
die Verl\"angerung ihrer G\"ultigkeit langsam verringern lassen.
%(siehe Abschnitt~\ref{sec:fees:coin})
\end{enumerate}
Das Taler-Bezahlsystem verwehrt den Nutzern kategorisch die Abhebung
von h\"oheren Betr\"agen als 5.000 \CURRENCY{} pro Monat bzw. von
mehr als 15.000 \CURRENCY{} pro Jahr. Damit wird gew\"ahrleistet,
dass die Nutzer stets unterhalb der Grenzwerte bleiben, ab denen die
meisten Pr\"ufschritte aufgrund regulatorischer Bestimmungen erforderlich
werden. \TALER{} stellt dar\"uber hinaus sicher, dass die Nutzer
ausschliesslich in \LAND{} ans\"assig sind
(siehe Abschnitt~\ref{sec:proc:domestic}), da auf ihrer Seite ein Bankkonto
in \LAND{} f\"ur die \"Uberweisungen an den Taler-Exchange und/oder
eine Telefonnummer mit entsprechender Vorwahl (++41) ben\"otigt werden.
Zus\"atzlich setzt das Taler-Wallet zu jeder Zeit eine Obergrenze
von 5.000 \CURRENCY{} auf die Coin-Betr\"age in Summe fest, so dass es
keine weitere Abhebung \"uber diesen Grenzwert hinaus bewirken kann.
F\"ur {\bf Verk\"aufer} beginnt ein gesch\"aftliches Nutzungsverh\"altnis
mit \TALER{}, sobald sie Geldeing\"ange auf ihren IBAN-Bankkonten erhalten,
die als Zahlungen von Nutzern des Taler-Bezahlsystems ausgel\"ost wurden
(siehe Abschnitt~\ref{sec:deposit}). Sollten die Summen der Eing\"ange
5.000 \CURRENCY{} pro Monat bzw. 15.000 \CURRENCY{} pro Jahr \"ubersteigen,
kommt es zu einer KYB-Pr\"ufung, die dem Begriff ``Er\"offnen'' eines
Kontos entspricht und die eine aktualisierte KYB-Information sowie
die Pr\"ufung von Sanktionslisten erfordert, sofern der Verk\"aufer
innerhalb von 24 Monaten wenigstens einen Geldeingang erhielt.
Im Gegensatz zu normalen Nutzern k\"onnen Verk\"aufer im Prinzip
Zahlungen ohne Limit empfangen. Allerdings m\"ussen diese Transaktionen
auch wirklich als Eing\"ange auf dem Bankkonto des Unternehmens verzeichnet
werden (im Kontoauszug). In Abh\"angigkeit von den an das Gesch\"aftskonto
\"uberwiesenen Betr\"agen wird der Verk\"aufer einer KYB-Pr\"ufung unterzogen
(siehe Abschnitt~\ref{sec:KYB}). Dies gilt ebenso f\"ur
Geldw\"asche-\"Uberpr\"ufungen (AML checks).
Das Taler-Bezahlsystem transferiert lediglich Gelder auf die bestehenden
Bankkonten der Verk\"aufer, die f\"ur ihre G\"uterleistungen Zahlungen
der Nutzer erhalten, f\"ur die bereits bei der \"Uberweisung von deren
Kundenkonten eine KYC-Pr\"ufung erfolgte. Daher wird unseres Erachtens
der Betreiber eines Taler-Exchange keine Mittelherkunft verlangen bzw.
nachweisen m\"ussen
\footnote{Wenn Unternehmen das Taler-Bezahlsystem ihrerseits f\"ur
Zahlungen nutzen wollen, m\"ussen sie genauso wie alle anderen Nutzer
zuerst Geld von ihrem Bankkonto an einen Taler-Exchange \"uberweisen,
eine KYC-Pr\"ufung absolvieren und dann ihr Wallet Coins abheben lassen.
F\"ur die gesch\"aftlichen K\"aufer gelten ebenfalls die Limits wie
f\"ur alle anderen Nutzer.}.
\include{int-withdraw}
\include{int-deposit}
\include{int-pay}
\include{int-refund}
\include{int-push}
\include{int-pull}
\include{int-shutdown}
\chapter{Regulatory Triggers} \label{chap:triggers}
In this chapter we show decision diagrams for regulatory processes of the
various core operations of the GNU Taler payment system. In each case, the
{\bf start} state refers to one of the interactions described in the previous
chapter. The payment system will then use the process to arrive at an {\bf
allow} decision which permits the transaction to go through, or at a {\bf
deny} decision which ensures that the funds are not moved.
The specific {\em decisions} (in green) depend on the risk profile and the
regulatory environment. The tables in each section list the specific values
that are to be configured.
There are five types if interactions that can trigger regulatory processes:
\begin{description}
\item[withdraw] a customer withdraws digital cash from their {\bf bank account}
\item[deposit] a merchant's {\bf bank account} is designated to receive a payment in digital cash
\item[push] a {\bf wallet} accepts a payment from another wallet
\item[pull] a {\bf wallet} requests a payment from another wallet
\item[balance] a withdraw or P2P payment causes the balance of a {\bf wallet} to exceed a given threshold
\end{description}
We note in bold the {\bf anchor} for the regulator process. The anchor is used
to link the interaction to an identity. Once an identity has been established
for a particular anchor, that link is considered established for all types of
activities involving that anchor. A wallet is uniquely identified in the
system by its unique cryptographic key. A bank account is uniquely identified
in the system by its (RFC 8905) bank routing data (usually including BIC, IBAN
and account owner name).
The KYC and AML processes themselves are described in
Chapter~\ref{chap:regproc}.
\include{kyc-withdraw}
\include{kyc-deposit}
\include{kyc-push}
\include{kyc-pull}
\include{kyc-balance}
\chapter{Regulatory Processes} \label{chap:regproc}
This chapter describes the interactions between the customer, exchange and
organizations or staff assisting with regulatory processes designed to ensure
that customers are residents in the area of operation of the payment service
provider, are properly identified, and do not engage in money laundering.
The three main regulatory processes are:
\begin{description}
\item[domestic check] This process establishes that a user is generally
eligible to use the payment system. The process checks that the user has an
eligible address, but stops short of establishing the user's identity.
\item[kyc] This process establishes a user's legal identity, possibly
using external providers to review documents and check against blacklists.
\item[aml] The AML process reviews suspicious payment activities for
money laundering. Here AML staff reviews all collected information.
\end{description}
\include{proc-domestic}
%\include{proc-kyc}
\include{proc-kyb}
\include{proc-aml}
\chapter{Fees} \label{chap:fees}
The business model for operating a Taler exchange is to charge transaction
fees. Fees are charged on certain operations by the exchange. There are two
types of fees, {\bf wire fees} and {\bf coin fees}. This chapter describes
the fee structure.
Fixed, amount-independent {\bf wire fees} are charged on wire transfers using
the core banking system. Details on wire fees are described in
Section~\ref{sec:fees:wire}.
Coin fees are more complex, as they do not exactly follow neither the usual
percentage of volume model of other payment systems. Instead, coin fees are
applied per coin, resulting in a {\em logarithmic} fee structure. As a
result, the effective fee {\em percentage} for tiny transactions is high (for
example 50\% for transactions of 0.0025 CHF) while the effective fee
percentage for large transactions is nominal (for example $\approx$ 0.05\% for
transactions of $\approx$ 40 CHF). Details on coin fees are described in
Section~\ref{sec:fees:coin}.
Fees are configurable (and that fee types beyond those described here are
supported by the software). Thus, the specific fees may be adjusted in the
future based on business decisions. However, changes to the fees are never
retroactively applied to coins already in circulation. Wire fees that have
been publicly announced for a particular time period also cannot be changed.
Finally, any change to the terms of service must also be explicitly accepted
by the users before they withdraw additional funds.
\include{fees-wire}
\include{fees-coins}
%\include{fees-other}
\end{document}

206
doc/flows/main.tex Normal file
View File

@ -0,0 +1,206 @@
\documentclass[10pt,a4paper,oneside]{book}
\usepackage[utf8]{inputenc}
\usepackage{url}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{qrcode}
\usepackage{pgf-umlsd}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usetikzlibrary{quotes}
\author{Christian Grothoff}
\title{Flows in the GNU Taler System}
\newcommand\CURRENCY{CHF}
\begin{document}
\maketitle
\tableofcontents
\chapter{Interactions} \label{chap:interactions}
This chapter introduces the main payment interactions in the GNU Taler payment
system. For each interaction, we introduce the parties involved and in which
order they interact and how. In each interaction it is possible that the
Taler exchange needs to trigger a compliance process. These regulatory
riggers are described in more detail in Chapter~\ref{chap:triggers}.
The main interactions of the system are:
\begin{description}
\item[withdraw] a customer withdraws digital cash to their wallet
\item[deposit] a customer returns digital cash into their bank account
\item[pay] a customer pays into bank account of a merchant
\item[refund] a merchant decides to return funds to a customer
\item[push] a customer sends a payment to another wallet
\item[pull] a customer requests a payment from another wallet (effectively sending an invoice)
\item[shutdown] the Taler payment system operator informs the customers that the system is being shut down for good
\end{description}
In the analysis of the legal requirements, it is important to differentiate
between transactions between wallets (customer-to-customer) and transactions
where money flows from a wallet into a bank account (customer-to-merchant) as
these have different limits: When digital coins are used to pay at a business in
Taler, the business never actually receives usable digital coins but instead
the amount is always directly credited to their bank account. Depending on
the transacted amounts, the business will nevertheless be subject to KYB
(Section~\ref{sec:proc:kyb}) and AML checks.
{\bf Customers} begin their business relationship with us when they withdraw
digital cash. Taler has no accounts (this is digital cash) and thus there is
no ``opening'' or ``closing'' of accounts for consumers. Given digital cash,
the customers can either (1) deposit the funds explicitly into a bank account
(see Section~\ref{sec:deposit}), (2) pay a merchant (see
Section~\ref{sec:pay}), (3) pay another customer using a peer-to-peer
transfer (see Sections~\ref{sec:push} and~\ref{sec:pull}), or (4) the coins
will expire if the wallet was lost (including offline for a long time or
uninstalled). Finally, if a wallet remains (occasionally) online but a user
does simply not spend the coins will (5) diminish in value from the change
fees (see Section~\ref{sec:fees:coin}) that apply to prevent the coins from
expiring outright.
For customers, we will categorically limit of digital cash withdrawn per month
to less than CHF 5'000 per month and less than CHF 25'000 per year, thus
ensuring that consumers remain below the thresholds where most regulatory
processes become applicable. Payments between users will be limited
to receiving less than CHF 1'000 per month and less than CHF 5'000 per year.
We will ensure that customers are Swiss
(see Section~\ref{sec:proc:domestic}) by requiring them to have a Swiss bank
account and/or Swiss phone number (+41-prefix).
%Furthermore, the wallet will
%impose an upper limit of CHF 5000 on its balance at any point in time.
For {\bf merchants}, the Taler equivalent of ``opening'' an account and thus
establishing an ongoing business relationship is for a business to receive
payments (see Section~\ref{sec:pay}) exceeding CHF 5'000/month or CHF
25'000/year. We will consider the account ``open'' (and require up-to-date KYB
information and check sanction lists) as long as the business has made any
transactions within the last 24 months.
As we will only transfer money into the existing bank accounts of the
merchants to compensate them for sales made using the Taler payment system, we
do not need to check the origin of funds for those merchants as they will only
receive funds from us.\footnote{Should businesses want to use Taler for
expenditures, they will need to withdraw digital coins from their bank account
just like customers, and the limits for customers will continue to apply.}
For individual {\bf transactions}, we will impose a limit of CHF
1'000/transaction (even though our reading of the regulations would permit
individual transactions up to CHF 15'000).
The following sections describe the respective processes for each of these
interactions.
\include{int-withdraw}
\include{int-deposit}
\include{int-pay}
\include{int-refund}
\include{int-push}
\include{int-pull}
\include{int-shutdown}
\chapter{Regulatory Triggers} \label{chap:triggers}
In this chapter we show decision diagrams for regulatory processes of the
various core operations of the GNU Taler payment system. In each case, the
{\bf start} state refers to one of the interactions described in the previous
chapter. The payment system will then use the process to arrive at an {\bf
allow} decision which permits the transaction to go through, or at a {\bf
deny} decision which ensures that the funds are not moved.
The specific {\em decisions} (in green) depend on the risk profile and the
regulatory environment. The tables in each section list the specific values
that are to be configured.
There are five types if interactions that can trigger regulatory processes:
\begin{description}
\item[withdraw] a customer withdraws digital cash from their {\bf bank account}
\item[deposit] a customer or merchant's {\bf bank account} is
designated to receive a payment due someone paying with or
depositing digital cash
\item[push] a {\bf wallet} accepts a payment from another wallet
\item[pull] a {\bf wallet} requests a payment from another wallet
% \item[balance] a withdraw or P2P payment causes the balance of a {\bf wallet} to exceed a given threshold
\end{description}
We note in bold the {\bf anchor} for the regulator process. The anchor is used
to link the interaction to an identity. Once an identity has been established
for a particular anchor, that link is considered established for all types of
activities involving that anchor. A wallet is uniquely identified in the
system by its unique cryptographic key. A bank account is uniquely identified
in the system by its (RFC 8905) bank routing data (usually including BIC, IBAN
and account owner name).
The KYC and AML processes themselves are described in
Chapter~\ref{chap:regproc}.
\include{kyc-withdraw}
\include{kyc-deposit}
\include{kyc-push}
\include{kyc-pull}
%\include{kyc-balance}
\chapter{Regulatory Processes} \label{chap:regproc}
This chapter describes the interactions between the customer, exchange and
organizations or staff assisting with regulatory processes designed to ensure
that customers are residents in the area of operation of the payment service
provider, are properly identified, and do not engage in money laundering.
The three main regulatory processes are:
\begin{description}
\item[domestic check] This process establishes that a user is generally
eligible to use the payment system. The process checks that the user has an
eligible address, but stops short of establishing the user's identity.
\item[kyc] This process establishes a user's legal identity, possibly
using external providers to review documents and check against blacklists.
\item[aml] The AML process reviews suspicious payment activities for
money laundering. Here AML staff reviews all collected information.
\end{description}
\include{proc-domestic}
\include{proc-kyc}
\include{proc-kyb}
\include{proc-aml}
\chapter{Fees} \label{chap:fees}
The business model for operating a Taler exchange is to charge transaction
fees. Fees are charged on certain operations by the exchange. There are two
types of fees, {\bf wire fees} and {\bf coin fees}. This chapter describes
the fee structure.
Fixed, amount-independent {\bf wire fees} are charged on wire transfers using
the core banking system. Details on wire fees are described in
Section~\ref{sec:fees:wire}.
Coin fees are more complex, as they do not exactly follow neither the usual
percentage of volume model of other payment systems. Instead, coin fees are
applied per coin, resulting in a {\em logarithmic} fee structure. As a
result, the effective fee {\em percentage} for tiny transactions is high (for
example 50\% for transactions of 0.0025 CHF) while the effective fee
percentage for large transactions is nominal (for example $\approx$ 0.05\% for
transactions of $\approx$ 40 CHF). Details on coin fees are described in
Section~\ref{sec:fees:coin}.
Fees are configurable (and that fee types beyond those described here are
supported by the software). Thus, the specific fees may be adjusted in the
future based on business decisions. However, changes to the fees are never
retroactively applied to coins already in circulation. Wire fees that have
been publicly announced for a particular time period also cannot be changed.
Finally, any change to the terms of service must also be explicitly accepted
by the users before they withdraw additional funds.
\include{fees-wire}
\include{fees-coins}
%\include{fees-other}
\end{document}

47
doc/flows/proc-aml.tex Normal file
View File

@ -0,0 +1,47 @@
\section{AML process}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{staff}{\shortstack{AML staff \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Access \\ Token};
\end{tikzpicture}
}}
\postlevel
\mess[0]{wallet}{{Initial action}}{exchange}
\begin{callself}{exchange}{Establish AML requirement}{}
\end{callself}
\begin{callself}{exchange}{Queue AML task}{}
\end{callself}
\mess[0]{exchange}{Wait for AML}{wallet}
\mess[0]{staff}{Request AML work}{exchange}
\mess[0]{exchange}{{Open AML task(s)}}{staff}
\mess[0]{staff}{Request details}{exchange}
\mess[0]{exchange}{KYC/AML data}{staff}
\begin{callself}{staff}{Review and decide}{}
\end{callself}
\mess[0]{staff}{{Decision documentation}}{exchange}
\mess[0]{exchange}{AML decision}{wallet}
\mess[0]{wallet}{{Retry action}}{exchange}
\end{sequencediagram}
\caption{Deposit interactions between customer, Taler exchange (payment
service provider) and the AML staff. The process can be
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.
AML staff interactions are cryptographically secured and
decisions and the provided reasoning are archived by the exchange.
AML staff may interact with the customer (out-of-band)
in its decision process.
}
\label{fig:proc:aml}
\end{figure}

View File

@ -0,0 +1,66 @@
\section{Domestic wallet check} \label{sec:proc:domestic}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer wallet \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{sms}{\shortstack{Address validator}}
\postlevel
\mess[0]{wallet}{{P2P payment (Wallet ID)}}{exchange}
\begin{callself}{exchange}{New wallet?}{}
\end{callself}
\mess[0]{exchange}{Request address validation}{sms}
\mess[0]{sms}{Validation process ID}{exchange}
\mess[0]{exchange}{Request address validation}{wallet}
\mess[0]{wallet}{Send address}{sms}
\mess[0]{sms}{{Send confirmation code (to address)}}{wallet}
\mess[0]{wallet}{Supply confirmation code}{sms}
\mess[0]{sms}{{Confirmed customer address}}{exchange}
\mess[0]{exchange}{{Confirm completion}}{wallet}
\mess[0]{wallet}{{Retry action}}{exchange}
\end{sequencediagram}
\caption{Deposit interactions between customer, Taler exchange (payment
service provider) and external address validation service. The process can be
triggered by wallet-to-wallet (P2P) payments described in Chapter~\ref{chap:triggers}.}
\label{fig:proc:domestic}
\end{figure}
Our users have to accept the terms of service which restrict the use of the
service to domestic customers. For interactions with the core banking system,
this simply means that we only accept payments from or to domestic bank
accounts. For P2P payments between wallets, we require that the wallets are
controlled by a domestic entity. We define domestic entities as those that
are able to receive messages at a domestic address. Two types of addresses are
supported:
\begin{itemize}
\item Control over a domestic {\bf mobile phone number} is established
by sending an SMS message with a confirmation code to the MSIN.
\item Control over a domestic {\bf postal address} is established by
sending a letter with a confirmation code to the address.
\end{itemize}
Depending on the type of address, a validation has a limited validity period,
as shown in Table~\ref{table:proc:domestic}. When the validity period is
over, a wallet has to re-do the address validation before they can receive any
further funds through the service.
\begin{table}[h!]
\caption{Restrictions on address validations}
\label{table:proc:domestic}
\begin{tabular}{l|l|r}
{\bf Type} & {\bf Validity period} & {\bf Restricted to} \\ \hline \hline
Mobile phone number & 12 months & {\em +41} \\
Postal address & 36 months & {\em Switzerland} \\
\end{tabular}
\end{table}

97
doc/flows/proc-kyb.tex Normal file
View File

@ -0,0 +1,97 @@
\section{KYB process} \label{sec:proc:kyb}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{merchant}{\shortstack{Merchant \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{kyb}{\shortstack{KYB provider \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\postlevel
\mess[0]{merchant}{{Initial action}}{exchange}
\begin{callself}{exchange}{Establish KYB requirement}{}
\end{callself}
\mess[0]{exchange}{Request new KYB process}{kyb}
\mess[0]{kyb}{{Process identifier (PI)}}{exchange}
\mess[0]{exchange}{{KYB required (PI)}}{merchant}
\mess[0]{merchant}{{KYB start (PI)}}{kyb}
\mess[0]{kyb}{{Request identity documentation}}{merchant}
\mess[0]{merchant}{{Upload identity documentation}}{kyb}
\begin{callself}{kyb}{Validate documentation}{}
\end{callself}
\mess[0]{kyb}{{Share documentation (PI)}}{exchange}
\mess[0]{kyb}{{Confirm completion}}{merchant}
\mess[0]{merchant}{{Retry action}}{exchange}
\end{sequencediagram}
\caption{Deposit interactions between customer, Taler exchange (payment
service provider) and external KYB provider. The process can be
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.}
\label{fig:proc:kyb}
\end{figure}
At the beginning of the KYB process, the user needs to specify whether they
are an {\bf individual} (not incorporated) or a {\bf business}.\footnote{In
practice, we expect most owners of bank accounts crossing the KYB threshold to
be businesses, but in principle such a bank account could be owned by an
individual operating a business without a separate legal entity.} This then
determines which types of attributes are collected in the KYB process
(Table~\ref{table:proc:kyb:individual}
vs. Table~\ref{table:proc:kyb:business}).
\begin{table}
\caption{Information collected for unincorporated individuals}
\label{table:proc:kyb:individual}
\begin{center}
\begin{tabular}{l|c|r}
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
Surname & yes & Mustermann \\
First name(s) & yes & Max \\
Date of birth & yes & 1.1.1980 \\
Nationality & yes & Swiss \\
Actual address of domicile & yes & Seestrasse 3, 8008 Zuerich \\
Phone number & no & +41-123456789 \\
E-mail & no & me@example.com \\
Identification document & yes & JPG image \\
Taxpayer identification & yes & ZPV Nr. 253'123'456 \\
\end{tabular}
\end{center}
\end{table}
\begin{table}
\caption{Information collected for businesses. Information on individals is
collected for owners with more than 25\% ownership and for those with
signature authority for the business.}
\label{table:proc:kyb:business}
\begin{center}
\begin{tabular}{l|c|r}
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
Company name & yes & Mega AG \\
Registered office & yes & Seestrasse 4, 8008 Zuerich \\
Company identification document & yes & PDF file \\
Power of attorney arrangement & yes & PDF file \\
Business registration number & yes & \\
Business registration document & yes & PDF file \\
Registration authority & yes & \\ \hline
Contact person name & yes & Max Mustermann \\
Identification document & yes & JPG image \\
Date of birth & yes & 1.1.1980 \\
Nationality & yes & Swiss \\
E-mail & yes & me@example.com \\
Phone number & no & +41-123456789 \\
\end{tabular}
\end{center}
\end{table}

88
doc/flows/proc-kyc.tex Normal file
View File

@ -0,0 +1,88 @@
\section{KYC process}
\begin{figure}[h!]
\begin{sequencediagram}
\newinst{wallet}{\shortstack{Customer \\
\\ \begin{tikzpicture}
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
\end{tikzpicture}
}}
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\newinst[2]{kyc}{\shortstack{KYC provider \\
\\ \begin{tikzpicture}[shape aspect=.5]
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
\end{tikzpicture}
}}
\postlevel
\mess[0]{wallet}{{Initial action}}{exchange}
\begin{callself}{exchange}{Establish KYC requirement}{}
\end{callself}
\mess[0]{exchange}{Request new KYC process}{kyc}
\mess[0]{kyc}{{Process identifier (PI)}}{exchange}
\mess[0]{exchange}{{KYC required (PI)}}{wallet}
\mess[0]{wallet}{{KYC start (PI)}}{kyc}
\mess[0]{kyc}{{Request identity documentation}}{wallet}
\mess[0]{wallet}{{Upload identity documentation}}{kyc}
\begin{callself}{kyc}{Validate documentation}{}
\end{callself}
\mess[0]{kyc}{{Share documentation (PI)}}{exchange}
\mess[0]{kyc}{{Confirm completion}}{wallet}
\mess[0]{wallet}{{Retry action}}{exchange}
\end{sequencediagram}
\caption{Deposit interactions between customer, Taler exchange (payment
service provider) and external KYC provider. The process can be
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.}
\label{fig:proc:kyc}
\end{figure}
At the beginning of the KYC process, the user needs to specify whether they
are an {\bf individual} or a {\bf business}.\footnote{ In practice, we expect
most wallet-users to be individuals, but in principle a wallet could be owned
by a business.} This then determines which types of attributes are collected
in the KYC process (Table~\ref{table:proc:kyc:individual} vs.
Table~\ref{table:proc:kyc:business}).
\begin{table}
\caption{Information collected for individuals}
\label{table:proc:kyc:individual}
\begin{center}
\begin{tabular}{l|c|r}
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
Surname & yes & Mustermann \\
First name(s) & yes & Max \\
Date of birth & yes & 1.1.1980 \\
Nationality & yes & Swiss \\
Actual address of domicile & yes & Seestrasse 3, 8008 Zuerich \\
Phone number & no & +41-123456789 \\
E-mail & no & me@example.com \\
Identification document & yes & JPG image \\
\end{tabular}
\end{center}
\end{table}
\begin{table}
\caption{Information collected for businesses}
\label{table:proc:kyc:business}
\begin{center}
\begin{tabular}{l|c|r}
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
Company name & yes & Mega AG \\
Registered office & yes & Seestrasse 4, 8008 Zuerich \\
Company identification document & yes & PDF file \\ \hline
Contact person name & yes & Max Mustermann \\
Phone number & no & +41-123456789 \\
E-mail & yes & me@example.com \\
Identification document & yes & JPG image \\
Date of birth & yes & 1.1.1980 \\
Nationality & yes & Swiss \\ \hline
Power of attorney arrangement & yes & PDF file \\
\end{tabular}
\end{center}
\end{table}

@ -1 +1 @@
Subproject commit 8452f991dd967328207fab52a99beb19e2cb4dff
Subproject commit 6026efb59ef8c41e5b86e68332780d387fdaab0a

View File

@ -218,6 +218,7 @@ EXTRA_DIST = \
taler-auditor.in \
taler-helper-auditor-render.py \
auditor.conf \
setup.sh \
test-sync-in.conf \
test-sync-out.conf \
generate-auditor-basedb.sh \

View File

@ -17,7 +17,7 @@ AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
# What is the Web site of the auditor (i.e. to file complaints about
# a misbehaving exchange)?
# BASE_URL = https://auditor.taler.net/
BASE_URL = http://localhost:8083/
# Network configuration for the normal API/service HTTP server

View File

@ -1,14 +1,27 @@
[exchange-offline]
MASTER_PRIV_FILE = auditor-basedb.mpriv
[PATHS]
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
TALER_CONFIG_HOME = $TALER_HOME/.config/taler/
TALER_DATA_HOME = $TALER_HOME/.local/share/taler/
TALER_HOME = ${PWD}/generate_auditordb_home/
[instance-default]
KEYFILE = ${TALER_DATA_HOME}/merchant/default.priv
NAME = Merchant Inc.
[taler]
CURRENCY = TESTKUDOS
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
[exchange]
MASTER_PUBLIC_KEY = M4FGP18EQFXFGGFQ1AWXHACN2JX0SMVK9CNF6459Z1WG18JSN0BG
SIGNKEY_DURATION = 4 weeks
LOOKAHEAD_SIGN = 32 weeks 1 day
SIGNKEY_LEGAL_DURATION = 4 weeks
AML_THRESHOLD = TESTKUDOS:1000000
[exchangedb-postgres]
CONFIG = postgres:///auditor-basedb
[exchange-account-1]
PAYTO_URI = payto://iban/SANDBOXX/DE989651?receiver-name=Exchange+Company
enable_debit = yes
enable_credit = yes
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
[exchange-accountcredentials-1]
WIRE_GATEWAY_URL = http://localhost:8082/facades/test-facade/taler-wire-gateway/
@ -16,24 +29,38 @@ WIRE_GATEWAY_AUTH_METHOD = basic
USERNAME = exchange
PASSWORD = x
[merchant-account-merchant]
PAYTO_URI = payto://x-taler-bank/localhost/42
HONOR_default = YES
ACTIVE_default = YES
[merchant]
WIREFORMAT = default
DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
WIRE_TRANSFER_DELAY = 1 minute
FORCE_AUDIT = YES
[merchantdb-postgres]
CONFIG = postgres:///auditor-basedb
[merchant-exchange-default]
MASTER_KEY = RKNMPRGXCX35H11WEYXDXYHPR7NX2QK9BG15MT0QEF75PC5KR470
MASTER_KEY = M4FGP18EQFXFGGFQ1AWXHACN2JX0SMVK9CNF6459Z1WG18JSN0BG
EXCHANGE_BASE_URL = http://localhost:8081/
CURRENCY = TESTKUDOS
[payments-generator]
currency = TESTKUDOS
instance = default
bank = http://localhost:8082/
merchant = http://localhost:9966/
exchange_admin = http://localhost:18080/
exchange-admin = http://localhost:18080/
exchange = http://localhost:8081/
[bank]
HTTP_PORT = 8082
[libeufin-nexus]
DB_CONNECTION="jdbc:postgresql://localhost/auditor-basedb?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
[libeufin-sandbox]
DB_CONNECTION="jdbc:postgresql://localhost/auditor-basedb?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
[auditor]
BASE_URL = http://localhost:8083/
TINY_AMOUNT = TESTKUDOS:0.01
PUBLIC_KEY = 0EHPW5WEKHXPPN4MPJNGA7Z6D29JP21GKVNV8ARFB1YW7WWJX20G
[auditordb-postgres]
CONFIG = postgres:///auditor-basedb
[coin_kudos_ct_1]
value = TESTKUDOS:0.01
@ -130,61 +157,3 @@ fee_refresh = TESTKUDOS:0.03
fee_refund = TESTKUDOS:0.01
CIPHER = RSA
rsa_keysize = 1024
[benchmark]
BANK_DETAILS = bank_details.json
MERCHANT_DETAILS = merchant_details.json
[arm]
CONFIG = /research/taler/exchange/src/auditor/auditor-basedb.conf
[taler]
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
CURRENCY = TESTKUDOS
AML_THRESHOLD = TESTKUDOS:1000000
[merchantdb-postgres]
CONFIG = postgres:///auditor-basedb
[merchant]
WIREFORMAT = default
DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
WIRE_TRANSFER_DELAY = 1 minute
FORCE_AUDIT = YES
UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
[exchangedb-postgres]
CONFIG = postgres:///auditor-basedb
[exchange]
MASTER_PUBLIC_KEY = RKNMPRGXCX35H11WEYXDXYHPR7NX2QK9BG15MT0QEF75PC5KR470
SIGNKEY_DURATION = 4 weeks
LOOKAHEAD_SIGN = 32 weeks 1 day
SIGNKEY_LEGAL_DURATION = 4 weeks
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
[bank]
HTTP_PORT = 8082
SUGGESTED_EXCHANGE = http://localhost:8081/
SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/localhost/2
ALLOW_REGISTRATIONS = YES
SERVE = http
MAX_DEBT_BANK = TESTKUDOS:100000.0
MAX_DEBT = TESTKUDOS:50.0
DATABASE = postgres:///auditor-basedb
[auditordb-postgres]
CONFIG = postgres:///auditor-basedb
[auditor]
BASE_URL = http://localhost:8083/
TINY_AMOUNT = TESTKUDOS:0.01
PUBLIC_KEY = 0EHPW5WEKHXPPN4MPJNGA7Z6D29JP21GKVNV8ARFB1YW7WWJX20G
[PATHS]
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
TALER_CONFIG_HOME = $TALER_HOME/.config/taler/
TALER_DATA_HOME = $TALER_HOME/.local/share/taler/
TALER_HOME = ${PWD}/generate_auditordb_home/

View File

@ -1,412 +1,61 @@
#!/bin/bash
# Script to generate the basic database for auditor
# testing from a 'correct' interaction between exchange,
# wallet and merchant.
# This file is in the public domain.
#
# Creates $BASEDB.sql, $BASEDB.fees,
# $BASEDB.{mpub,mpriv}.
# Default $BASEDB is "auditor-basedb", override via $1.
# Script to generate the basic database for auditor testing from a 'correct'
# interaction between exchange, wallet and merchant.
#
# Currently must be run online as it interacts with
# bank.test.taler.net; also requires the wallet CLI
# to be installed and in the path. Furthermore, the
# user running this script must be Postgres superuser
# and be allowed to create/drop databases.
# Creates "$1.sql".
#
# Requires the wallet CLI to be installed and in the path. Furthermore, the
# user running this script must be Postgres superuser and be allowed to
# create/drop databases.
#
set -eu
#set -x
# Cleanup to run whenever we exit
function exit_cleanup()
{
echo "Running generate-auditor-basedb exit cleanup logic..."
if test -f ${MY_TMP_DIR:-/}/libeufin-sandbox.pid
then
PID=`cat ${MY_TMP_DIR}/libeufin-sandbox.pid 2> /dev/null`
kill $PID 2> /dev/null || true
rm ${MY_TMP_DIR}/libeufin-sandbox.pid
echo "Killed libeufin sandbox $PID"
wait $PID || true
fi
if test -f ${MY_TMP_DIR:-/}/libeufin-nexus.pid
then
PID=`cat ${MY_TMP_DIR}/libeufin-nexus.pid 2> /dev/null`
kill $PID 2> /dev/null || true
rm ${MY_TMP_DIR}/libeufin-nexus.pid
echo "Killed libeufin nexus $PID"
wait $PID || true
fi
echo "killing libeufin DONE"
for n in `jobs -p`
do
kill $n 2> /dev/null || true
done
wait || true
}
# Install cleanup handler (except for kill -9)
trap exit_cleanup EXIT
# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
echo "SKIPPING: $1"
exit 77
}
# Where do we write the result?
BASEDB=${1:-"auditor-basedb"}
# Name of the Postgres database we will use for the script.
# Will be dropped, do NOT use anything that might be used
# elsewhere
export TARGET_DB=`basename ${BASEDB}`
BASEDB="$1"
export WALLET_DB=${BASEDB:-"wallet"}.wdb
. setup.sh
# delete existing wallet database
rm -f $WALLET_DB
# Configuration file will be edited, so we create one
# from the template.
export CONF=$1.conf
cp generate-auditor-basedb.conf $CONF
echo "Created configuration at ${CONF}"
DATA_DIR=$1/exchange-data-dir/
mkdir -p $DATA_DIR
taler-config -c $CONF -s PATHS -o TALER_HOME -V $DATA_DIR
echo -n "Testing for libeufin"
libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
echo -n "Testing for taler-wallet-cli"
taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
echo -n "Testing for curl"
echo -n "Testing for curl ..."
curl --help >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
CONF="generate-auditor-basedb.conf"
# reset database
dropdb $TARGET_DB >/dev/null 2>/dev/null || true
createdb $TARGET_DB || exit_skip "Could not create database $TARGET_DB"
ORIGIN=`pwd`
MY_TMP_DIR=`dirname $1`
echo -n "Reset 'auditor-basedb' database ..."
dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB'"
echo " DONE"
# Launch exchange, merchant and bank.
setup -c "$CONF" \
-aenmsw \
-d "iban"
# obtain key configuration data
MASTER_PRIV_FILE=$1.mpriv
MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
taler-config -f -c ${CONF} -s exchange-offline -o MASTER_PRIV_FILE -V ${MASTER_PRIV_FILE}
rm -f "${MASTER_PRIV_FILE}"
mkdir -p $MASTER_PRIV_DIR
gnunet-ecc -l/dev/null -g1 $MASTER_PRIV_FILE > /dev/null
export MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
export EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
export MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
EXCHANGE_URL=$(taler-config -c "$CONF" -s EXCHANGE -o BASE_URL)
MERCHANT_PORT=$(taler-config -c "$CONF" -s MERCHANT -o PORT)
MERCHANT_URL="http://localhost:${MERCHANT_PORT}/"
BANK_PORT=$(taler-config -c "$CONF" -s BANK -o HTTP_PORT)
BANK_URL="http://localhost:1${BANK_PORT}"
export AUDITOR_URL=http://localhost:8083/
AUDITOR_PRIV_FILE=$1.apriv
AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
taler-config -f -c ${CONF} -s auditor -o AUDITOR_PRIV_FILE -V ${AUDITOR_PRIV_FILE}
mkdir -p $AUDITOR_PRIV_DIR
gnunet-ecc -l/dev/null -g1 $AUDITOR_PRIV_FILE > /dev/null
AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
echo "MASTER PUB is ${MASTER_PUB} using file ${MASTER_PRIV_FILE}"
echo "AUDITOR PUB is ${AUDITOR_PUB} using file ${AUDITOR_PRIV_FILE}"
# patch configuration
taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
taler-config -c $CONF -s auditor -o PUBLIC_KEY -V $AUDITOR_PUB
taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TARGET_DB
taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TARGET_DB
taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TARGET_DB
taler-config -c $CONF -s bank -o database -V postgres:///$TARGET_DB
# setup exchange
echo "Setting up exchange"
taler-exchange-dbinit -c $CONF
echo "Setting up merchant"
taler-merchant-dbinit -c $CONF
# setup auditor
echo "Setting up auditor"
taler-auditor-dbinit -c $CONF || exit_skip "Failed to initialize auditor DB"
taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL || exit_skip "Failed to add exchange to auditor"
# Launch services
echo "Launching services (pre audit DB: $TARGET_DB)"
rm -rf ${TARGET_DB}-sandbox.sqlite3
export LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-sandbox.sqlite3"
# Create the default demobank.
cd $MY_TMP_DIR
export LIBEUFIN_SANDBOX_ADMIN_PASSWORD=secret
libeufin-sandbox config --currency "TESTKUDOS" default
libeufin-sandbox serve --port "1${BANK_PORT}" \
> ${MY_TMP_DIR}/libeufin-sandbox-stdout.log \
2> ${MY_TMP_DIR}/libeufin-sandbox-stderr.log &
echo $! > ${MY_TMP_DIR}/libeufin-sandbox.pid
cd $ORIGIN
export LIBEUFIN_SANDBOX_URL="http://localhost:1${BANK_PORT}"
set +e
echo -n "Waiting for Sandbox..."
OK=0
for n in `seq 1 100`; do
echo -n "."
sleep 1
if wget --timeout=1 \
--user admin --password secret --auth-no-challenge \
--tries=3 --waitretry=0 \
-o /dev/null -O /dev/null \
${LIBEUFIN_SANDBOX_URL};
then
OK=1
break
fi
done
if test $OK != 1
then
exit_skip " Failed to launch sandbox"
fi
echo "OK"
register_sandbox_account() {
export LIBEUFIN_SANDBOX_USERNAME=$1
export LIBEUFIN_SANDBOX_PASSWORD=$2
cd $MY_TMP_DIR
libeufin-cli sandbox \
demobank \
register --name "$3"
cd $ORIGIN
unset LIBEUFIN_SANDBOX_USERNAME
unset LIBEUFIN_SANDBOX_PASSWORD
}
set -e
echo -n "Register the 'fortytwo' Sandbox user.."
register_sandbox_account fortytwo x "Forty Two"
echo OK
echo -n "Register the 'fortythree' Sandbox user.."
register_sandbox_account fortythree x "Forty Three"
echo OK
echo -n "Register 'exchange' Sandbox user.."
register_sandbox_account exchange x "Exchange Company"
echo OK
echo -n "Specify exchange's PAYTO_URI in the config ..."
export LIBEUFIN_SANDBOX_USERNAME=exchange
export LIBEUFIN_SANDBOX_PASSWORD=x
cd $MY_TMP_DIR
PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI -V $PAYTO
echo " OK"
echo -n "Setting this exchange as the bank's default ..."
EXCHANGE_PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
libeufin-sandbox default-exchange "$EXCHANGE_URL" "$EXCHANGE_PAYTO"
echo " OK"
# Prepare EBICS: create Ebics host and Exchange subscriber.
# Shortly becoming admin to setup Ebics.
export LIBEUFIN_SANDBOX_USERNAME=admin
export LIBEUFIN_SANDBOX_PASSWORD=secret
echo -n "Create EBICS host at Sandbox.."
libeufin-cli sandbox \
--sandbox-url "http://localhost:1${BANK_PORT}" \
ebicshost create --host-id "talerebics"
echo "OK"
echo -n "Create exchange EBICS subscriber at Sandbox.."
libeufin-cli sandbox \
demobank new-ebicssubscriber --host-id talerebics \
--user-id exchangeebics --partner-id talerpartner \
--bank-account exchange # that's a username _and_ a bank account name
echo "OK"
unset LIBEUFIN_SANDBOX_USERNAME
unset LIBEUFIN_SANDBOX_PASSWORD
# Prepare Nexus, which is the side actually talking
# to the exchange.
rm -rf ${TARGET_DB}-nexus.sqlite3
export LIBEUFIN_NEXUS_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-nexus.sqlite3"
# For convenience, username and password are
# identical to those used at the Sandbox.
echo -n "Create exchange Nexus user..."
libeufin-nexus superuser exchange --password x
echo " OK"
libeufin-nexus serve --port ${BANK_PORT} \
2> ${MY_TMP_DIR}/libeufin-nexus-stderr.log \
> ${MY_TMP_DIR}/libeufin-nexus-stdout.log &
echo $! > ${MY_TMP_DIR}/libeufin-nexus.pid
export LIBEUFIN_NEXUS_URL="http://localhost:${BANK_PORT}"
echo -n "Waiting for Nexus..."
set +e
OK=0
for n in `seq 1 50`; do
echo -n "."
sleep 1
if wget --timeout=1 \
--tries=3 --waitretry=0 \
-o /dev/null -O /dev/null \
$LIBEUFIN_NEXUS_URL;
then
OK=1
break
fi
done
if test $OK != 1
then
exit_skip " Failed to launch Nexus at $LIBEUFIN_NEXUS_URL"
fi
set -e
echo "OK"
export LIBEUFIN_NEXUS_USERNAME=exchange
export LIBEUFIN_NEXUS_PASSWORD=x
echo -n "Creating an EBICS connection at Nexus..."
libeufin-cli connections new-ebics-connection \
--ebics-url "http://localhost:1${BANK_PORT}/ebicsweb" \
--host-id "talerebics" \
--partner-id "talerpartner" \
--ebics-user-id "exchangeebics" \
talerconn
echo "OK"
echo -n "Setup EBICS keying..."
libeufin-cli connections connect "talerconn" > /dev/null
echo "OK"
echo -n "Download bank account name from Sandbox..."
libeufin-cli connections download-bank-accounts "talerconn"
echo "OK"
echo -n "Importing bank account info into Nexus..."
libeufin-cli connections import-bank-account \
--offered-account-id "exchange" \
--nexus-bank-account-id "exchange-nexus" \
"talerconn"
echo "OK"
echo -n "Setup payments submission task..."
# Tries every second.
libeufin-cli accounts task-schedule \
--task-type submit \
--task-name "exchange-payments" \
--task-cronspec "* * *" \
"exchange-nexus"
echo "OK"
# Tries every second. Ask C52
echo -n "Setup history fetch task..."
libeufin-cli accounts task-schedule \
--task-type fetch \
--task-name "exchange-history" \
--task-cronspec "* * *" \
--task-param-level report \
--task-param-range-type latest \
"exchange-nexus"
echo "OK"
# create Taler facade.
echo -n "Create the Taler facade at Nexus..."
libeufin-cli facades \
new-taler-wire-gateway-facade \
--currency "TESTKUDOS" --facade-name "test-facade" \
"talerconn" "exchange-nexus"
echo "OK"
cd $ORIGIN
# Facade schema: http://localhost:$BANK_PORT/facades/test-facade/taler-wire-gateway/
TFN=`which taler-exchange-httpd`
TBINPFX=`dirname $TFN`
TLIBEXEC=${TBINPFX}/../lib/taler/libexec/
taler-exchange-secmod-eddsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-eddsa.log &
taler-exchange-secmod-rsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-rsa.log &
taler-exchange-secmod-cs -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-cs.log &
taler-exchange-httpd -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-httpd.log &
taler-merchant-httpd -c $CONF -L INFO 2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
taler-exchange-wirewatch -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-wirewatch.log &
taler-auditor-httpd -L INFO -c $CONF 2> ${MY_TMP_DIR}/taler-auditor-httpd.log &
export BANK_PORT
export EXCHANGE_URL
export MERCHANT_URL
export AUDITOR_URL
echo -n "Waiting for services to be available "
# Wait for all bank to be available (usually the slowest)
for n in `seq 1 50`
do
echo -n "."
sleep 0.2
OK=0
# bank
wget http://localhost:${BANK_PORT}/ -o /dev/null -O /dev/null >/dev/null || continue
OK=1
break
done
if [ 1 != $OK ]
then
exit_skip "Failed to launch services (bank)"
fi
# Wait for all services to be available
for n in `seq 1 50`
do
echo -n "."
sleep 0.1
OK=0
# exchange
wget ${EXCHANGE_URL}seed -o /dev/null -O /dev/null >/dev/null || continue
# merchant
wget ${MERCHANT_URL} -o /dev/null -O /dev/null >/dev/null || continue
# Auditor
wget ${AUDITOR_URL} -o /dev/null -O /dev/null >/dev/null || continue
OK=1
break
done
if [ 1 != $OK ]
then
bash
exit_skip "Failed to launch services (Taler)"
fi
echo -n "Setting up keys"
taler-exchange-offline -c $CONF \
download sign \
enable-account `taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI` \
enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
wire-fee now iban TESTKUDOS:0.07 TESTKUDOS:0.01 \
global-fee now TESTKUDOS:0.01 TESTKUDOS:0.01 TESTKUDOS:0.01 1h 1year 5 \
upload &> ${MY_TMP_DIR}/taler-exchange-offline.log
echo -n "."
for n in `seq 1 2`
do
echo -n "."
OK=0
wget --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
OK=1
break
done
if [ 1 != $OK ]
then
exit_skip "Failed to setup keys"
fi
echo " DONE"
echo -n "Adding auditor signatures ..."
taler-auditor-offline -c $CONF \
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
echo " DONE"
# Setup merchant
echo -n "Setting up merchant"
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
echo -n "Setting up merchant ..."
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' "${MERCHANT_URL}management/instances"
echo " DONE"
# run wallet CLI
echo "Running wallet"
# delete existing wallet database
export WALLET_DB="wallet.wdb"
rm -f "$WALLET_DB"
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'runIntegrationTest' \
echo -n "Running wallet ..."
taler-wallet-cli \
--no-throttle \
--wallet-db="$WALLET_DB" \
api \
--expect-success \
'runIntegrationTest' \
"$(jq -n '
{
amountToSpend: "TESTKUDOS:4",
@ -418,28 +67,25 @@ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'runI
--arg MERCHANT_URL "$MERCHANT_URL" \
--arg EXCHANGE_URL "$EXCHANGE_URL" \
--arg BANK_URL "$BANK_URL/demobanks/default/access-api/"
)" &> ${MY_TMP_DIR}/taler-wallet-cli.log
echo "Shutting down services"
exit_cleanup
)" &> taler-wallet-cli.log
echo " DONE"
# Dump database
echo "Dumping database ${BASEDB}(-libeufin).sql"
pg_dump -O $TARGET_DB | sed -e '/AS integer/d' > ${BASEDB}.sql
cd $MY_TMP_DIR
sqlite3 ${TARGET_DB}-nexus.sqlite3 ".dump" > ${BASEDB}-libeufin-nexus.sql
sqlite3 ${TARGET_DB}-sandbox.sqlite3 ".dump" > ${BASEDB}-libeufin-sandbox.sql
rm ${TARGET_DB}-sandbox.sqlite3 ${TARGET_DB}-nexus.sqlite3 # libeufin DB
cd $ORIGIN
mkdir -p "$(dirname "$BASEDB")"
echo $MASTER_PUB > ${BASEDB}.mpub
echo "Dumping database ${BASEDB}.sql"
pg_dump -O "auditor-basedb" | sed -e '/AS integer/d' > "${BASEDB}.sql"
# clean up
echo "Final clean up"
dropdb $TARGET_DB
echo -n "Final clean up ..."
kill -TERM "$SETUP_PID"
wait
unset SETUP_PID
dropdb "auditor-basedb"
echo " DONE"
echo "====================================="
echo " Finished generation of $BASEDB"
echo "Finished generation of ${BASEDB}.sql"
echo "====================================="
exit 0

View File

@ -8,405 +8,50 @@
set -eu
# set -x
# Cleanup to run whenever we exit
function exit_cleanup()
{
echo "Running generate-revoke-basedb exit cleanup logic..."
if test -f ${MY_TMP_DIR:-/}/libeufin-sandbox.pid
then
PID=`cat ${MY_TMP_DIR}/libeufin-sandbox.pid 2> /dev/null`
kill $PID 2> /dev/null || true
rm ${MY_TMP_DIR}/libeufin-sandbox.pid
echo "Killed libeufin sandbox $PID"
wait $PID || true
fi
if test -f ${MY_TMP_DIR}/libeufin-nexus.pid
then
PID=`cat ${MY_TMP_DIR}/libeufin-nexus.pid 2> /dev/null`
kill $PID 2> /dev/null || true
rm ${MY_TMP_DIR}/libeufin-nexus.pid
echo "Killed libeufin nexus $PID"
wait $PID || true
fi
echo "killing libeufin DONE"
for n in `jobs -p`
do
kill $n 2> /dev/null || true
done
wait
}
. setup.sh
function get_payto_uri() {
export LIBEUFIN_SANDBOX_USERNAME=$1
export LIBEUFIN_SANDBOX_PASSWORD=$2
export LIBEUFIN_SANDBOX_URL=$BANK_URL
cd $MY_TMP_DIR
libeufin-cli sandbox demobank info --bank-account $1 | jq --raw-output '.paytoUri'
cd $ORIGIN
}
# Install cleanup handler (except for kill -9)
trap exit_cleanup EXIT
# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
echo $1
exit 77
}
# Where do we write the result?
export BASEDB=${1:-"revoke-basedb"}
# Name of the Postgres database we will use for the script.
# Will be dropped, do NOT use anything that might be used
# elsewhere
export TARGET_DB=`basename ${BASEDB}`
TMP_DIR=`mktemp -d revocation-tmp-XXXXXX`
export WALLET_DB=wallet-revocation.json
rm -f $WALLET_DB
# Configuration file will be edited, so we create one
# from the template.
export CONF=${BASEDB}.conf
cp generate-auditor-basedb.conf $CONF
echo "Created configuration at ${CONF}"
DATA_DIR=$1/exchange-data-dir/
mkdir -p $DATA_DIR
taler-config -c $CONF -s PATHS -o TALER_HOME -V $DATA_DIR
echo -n "Testing for libeufin(-cli)"
libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
echo -n "Testing for taler-wallet-cli"
taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
echo -n "Testing for curl"
echo -n "Testing for curl ..."
curl --help >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
# reset database
dropdb $TARGET_DB >/dev/null 2>/dev/null || true
createdb $TARGET_DB || exit_skip "Could not create database $TARGET_DB"
ORIGIN=`pwd`
MY_TMP_DIR=`dirname $1`
CONF="generate-auditor-basedb.conf"
# reset database
echo -n "Reset 'auditor-basedb' database ..."
dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB'"
echo " DONE"
# Launch exchange, merchant and bank.
setup -c "$CONF" \
-aenmsw \
-d "iban"
# obtain key configuration data
MASTER_PRIV_FILE=$1.mpriv
MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
taler-config -f -c $CONF -s exchange-offline -o MASTER_PRIV_FILE -V ${MASTER_PRIV_FILE}
mkdir -p $MASTER_PRIV_DIR
rm -f "${MASTER_PRIV_FILE}"
gnunet-ecc -g1 $MASTER_PRIV_FILE > /dev/null
export MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
export EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
export MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
export BANK_URL=http://localhost:1${BANK_PORT}
export AUDITOR_URL=http://localhost:8083/
AUDITOR_PRIV_FILE=$1.apriv
AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
taler-config -f -c ${CONF} -s auditor -o AUDITOR_PRIV_FILE -V ${AUDITOR_PRIV_FILE}
mkdir -p $AUDITOR_PRIV_DIR
gnunet-ecc -l /dev/null -g1 $AUDITOR_PRIV_FILE > /dev/null
AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
EXCHANGE_URL=$(taler-config -c "$CONF" -s EXCHANGE -o BASE_URL)
MERCHANT_PORT=$(taler-config -c "$CONF" -s MERCHANT -o PORT)
MERCHANT_URL="http://localhost:${MERCHANT_PORT}/"
BANK_PORT=$(taler-config -c "$CONF" -s BANK -o HTTP_PORT)
BANK_URL="http://localhost:1${BANK_PORT}"
echo "MASTER PUB is ${MASTER_PUB} using file ${MASTER_PRIV_FILE}"
echo "AUDITOR PUB is ${AUDITOR_PUB} using file ${AUDITOR_PRIV_FILE}"
# patch configuration
taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
taler-config -c $CONF -s auditor -o PUBLIC_KEY -V $AUDITOR_PUB
taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TARGET_DB
taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TARGET_DB
taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TARGET_DB
taler-config -c $CONF -s bank -o database -V postgres:///$TARGET_DB
taler-config -c $CONF -s exchange -o KEYDIR -V "${TMP_DIR}/keydir/"
taler-config -c $CONF -s exchange -o REVOCATION_DIR -V "${TMP_DIR}/revdir/"
# setup exchange
echo "Setting up exchange"
taler-exchange-dbinit -c $CONF
echo "Setting up merchant"
taler-merchant-dbinit -c $CONF
# setup auditor
echo "Setting up auditor"
taler-auditor-dbinit -c $CONF
taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL
# Launch services
echo "Launching services"
export LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-sandbox.sqlite3"
# Create the default demobank.
cd $MY_TMP_DIR
export LIBEUFIN_SANDBOX_ADMIN_PASSWORD=secret
libeufin-sandbox config --currency "TESTKUDOS" default
libeufin-sandbox serve --port "1${BANK_PORT}" \
> ${MY_TMP_DIR}/libeufin-sandbox-stdout.log \
2> ${MY_TMP_DIR}/libeufin-sandbox-stderr.log &
echo $! > ${MY_TMP_DIR}/libeufin-sandbox.pid
cd $ORIGIN
export LIBEUFIN_SANDBOX_URL="http://localhost:1${BANK_PORT}"
set +e
echo -n "Waiting for Sandbox..."
OK=0
for n in `seq 1 50`; do
echo -n "."
sleep 1
if wget --timeout=1 \
--user admin --password secret --auth-no-challenge \
--tries=3 --waitretry=0 \
-o /dev/null -O /dev/null \
${LIBEUFIN_SANDBOX_URL};
then
OK=1
break
fi
done
if test $OK != 1
then
exit_skip " Failed to launch sandbox"
fi
echo "OK"
register_sandbox_account() {
export LIBEUFIN_SANDBOX_USERNAME=$1
export LIBEUFIN_SANDBOX_PASSWORD=$2
cd $MY_TMP_DIR
libeufin-cli sandbox \
demobank \
register --name "$3"
cd $ORIGIN
unset LIBEUFIN_SANDBOX_USERNAME
unset LIBEUFIN_SANDBOX_PASSWORD
}
set -e
echo -n "Register the 'fortytwo' Sandbox user.."
register_sandbox_account fortytwo x "Forty Two"
echo OK
echo -n "Register the 'fortythree' Sandbox user.."
register_sandbox_account fortythree x "Forty Three"
echo OK
echo -n "Register 'exchange' Sandbox user.."
register_sandbox_account exchange x "Exchange Company"
echo OK
echo -n "Specify exchange's PAYTO_URI in the config ..."
export LIBEUFIN_SANDBOX_USERNAME=exchange
export LIBEUFIN_SANDBOX_PASSWORD=x
cd $MY_TMP_DIR
PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI -V $PAYTO
echo " OK"
echo -n "Setting this exchange as the bank's default ..."
EXCHANGE_PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
libeufin-sandbox default-exchange "$EXCHANGE_URL" "$EXCHANGE_PAYTO"
echo " OK"
# Prepare EBICS: create Ebics host and Exchange subscriber.
# Shortly becoming admin to setup Ebics.
export LIBEUFIN_SANDBOX_USERNAME=admin
export LIBEUFIN_SANDBOX_PASSWORD=secret
echo -n "Create EBICS host at Sandbox.."
libeufin-cli sandbox \
--sandbox-url "http://localhost:1${BANK_PORT}" \
ebicshost create --host-id "talerebics"
echo "OK"
echo -n "Create exchange EBICS subscriber at Sandbox.."
libeufin-cli sandbox \
demobank new-ebicssubscriber --host-id talerebics \
--user-id exchangeebics --partner-id talerpartner \
--bank-account exchange # that's a username _and_ a bank account name
echo "OK"
unset LIBEUFIN_SANDBOX_USERNAME
unset LIBEUFIN_SANDBOX_PASSWORD
# Prepare Nexus, which is the side actually talking
# to the exchange.
export LIBEUFIN_NEXUS_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-nexus.sqlite3"
# For convenience, username and password are
# identical to those used at the Sandbox.
echo -n "Create exchange Nexus user..."
libeufin-nexus superuser exchange --password x
echo " OK"
libeufin-nexus serve --port ${BANK_PORT} \
2> ${MY_TMP_DIR}/libeufin-nexus-stderr.log \
> ${MY_TMP_DIR}/libeufin-nexus-stdout.log &
echo $! > ${MY_TMP_DIR}/libeufin-nexus.pid
export LIBEUFIN_NEXUS_URL="http://localhost:${BANK_PORT}"
echo -n "Waiting for Nexus..."
set +e
OK=0
for n in `seq 1 50`; do
echo -n "."
sleep 1
if wget --timeout=1 \
--tries=3 --waitretry=0 \
-o /dev/null -O /dev/null \
$LIBEUFIN_NEXUS_URL;
then
OK=1
break
fi
done
if test $OK != 1
then
exit_skip " Failed to launch Nexus at $LIBEUFIN_NEXUS_URL"
fi
set -e
echo "OK"
export LIBEUFIN_NEXUS_USERNAME=exchange
export LIBEUFIN_NEXUS_PASSWORD=x
echo -n "Creating an EBICS connection at Nexus..."
libeufin-cli connections new-ebics-connection \
--ebics-url "http://localhost:1${BANK_PORT}/ebicsweb" \
--host-id "talerebics" \
--partner-id "talerpartner" \
--ebics-user-id "exchangeebics" \
talerconn
echo "OK"
echo -n "Setup EBICS keying..."
libeufin-cli connections connect "talerconn" > /dev/null
echo "OK"
echo -n "Download bank account name from Sandbox..."
libeufin-cli connections download-bank-accounts "talerconn"
echo "OK"
echo -n "Importing bank account info into Nexus..."
libeufin-cli connections import-bank-account \
--offered-account-id "exchange" \
--nexus-bank-account-id "exchange-nexus" \
"talerconn"
echo "OK"
echo -n "Setup payments submission task..."
# Tries every second.
libeufin-cli accounts task-schedule \
--task-type submit \
--task-name "exchange-payments" \
--task-cronspec "* * *" \
"exchange-nexus"
echo "OK"
# Tries every second. Ask C52
echo -n "Setup history fetch task..."
libeufin-cli accounts task-schedule \
--task-type fetch \
--task-name "exchange-history" \
--task-cronspec "* * *" \
--task-param-level report \
--task-param-range-type latest \
"exchange-nexus"
echo "OK"
# create Taler facade.
echo -n "Create the Taler facade at Nexus..."
libeufin-cli facades \
new-taler-wire-gateway-facade \
--currency "TESTKUDOS" --facade-name "test-facade" \
"talerconn" "exchange-nexus"
echo "OK"
cd $ORIGIN
# Facade schema: http://localhost:$BANK_PORT/facades/test-facade/taler-wire-gateway/
TFN=`which taler-exchange-httpd`
TBINPFX=`dirname $TFN`
TLIBEXEC=${TBINPFX}/../lib/taler/libexec/
taler-exchange-secmod-eddsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-eddsa.log &
SIGNKEY_HELPER_PID=$!
taler-exchange-secmod-rsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-rsa.log &
RSA_DENOM_HELPER_PID=$!
taler-exchange-secmod-cs -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-cs.log &
CS_DENOM_HELPER_PID=$!
taler-exchange-httpd -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-httpd.log &
EXCHANGE_PID=$!
taler-merchant-httpd -c $CONF -L INFO 2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
MERCHANT_PID=$!
taler-exchange-wirewatch -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-wirewatch.log &
taler-auditor-httpd -c $CONF 2> ${MY_TMP_DIR}/taler-auditor-httpd.log &
# Wait for all bank to be available (usually the slowest)
for n in `seq 1 50`
do
echo -n "."
sleep 0.2
OK=0
# bank
wget http://localhost:8082/ -o /dev/null -O /dev/null >/dev/null || continue
OK=1
break
done
if [ 1 != $OK ]
then
exit_skip "Failed to launch Bank services"
fi
# Wait for all other services to be available
for n in `seq 1 50`
do
echo -n "."
sleep 0.1
OK=0
# exchange
wget http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null || continue
# merchant
wget http://localhost:9966/ -o /dev/null -O /dev/null >/dev/null || continue
# Auditor
wget http://localhost:8083/ -o /dev/null -O /dev/null >/dev/null || continue
OK=1
break
done
if [ 1 != $OK ]
then
exit_cleanup
exit_skip "Failed to launch Taler services"
fi
echo " DONE"
echo -n "Setting up keys"
taler-exchange-offline -c $CONF \
download sign \
enable-account `taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI` \
enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
wire-fee now iban TESTKUDOS:0.01 TESTKUDOS:0.01 \
global-fee now TESTKUDOS:0.01 TESTKUDOS:0.01 TESTKUDOS:0.01 1h 1year 5 \
upload &> ${MY_TMP_DIR}/taler-exchange-offline.log
echo -n "."
for n in `seq 1 2`
do
echo -n "."
OK=0
# bank
wget --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
OK=1
break
done
if [ 1 != $OK ]
then
exit_skip "Failed to setup keys"
fi
taler-auditor-offline -c $CONF \
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
echo " DONE"
# Setup merchant
echo -n "Setting up merchant"
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
echo -n "Setting up merchant ..."
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' "${MERCHANT_URL}management/instances"
echo " DONE"
# run wallet CLI
echo "Running wallet"
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'withdrawTestBalance' \
export WALLET_DB="wallet.wdb"
rm -f "$WALLET_DB"
taler-wallet-cli \
--no-throttle \
--wallet-db="$WALLET_DB" \
api \
--expect-success 'withdrawTestBalance' \
"$(jq -n '
{
amount: "TESTKUDOS:8",
@ -414,57 +59,85 @@ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'with
exchangeBaseUrl: $EXCHANGE_URL,
}' \
--arg BANK_URL "$BANK_URL/demobanks/default/access-api/" \
--arg EXCHANGE_URL $EXCHANGE_URL
)"
--arg EXCHANGE_URL "$EXCHANGE_URL"
)" &> taler-wallet-cli-withdraw.log
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB run-until-done
taler-wallet-cli \
--no-throttle \
--wallet-db="$WALLET_DB" \
run-until-done \
&> taler-wallet-cli-withdraw-finish.log
export coins=$(taler-wallet-cli --wallet-db=$WALLET_DB advanced dump-coins)
export COINS=$(taler-wallet-cli --wallet-db="$WALLET_DB" advanced dump-coins)
echo -n "COINS are:"
echo $coins
echo "$COINS"
# Find coin we want to revoke
export rc=$(echo "$coins" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .coin_pub')
export rc=$(echo "$COINS" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .coin_pub')
# Find the denom
export rd=$(echo "$coins" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .denom_pub_hash')
echo "Revoking denomination ${rd} (to affect coin ${rc})"
export rd=$(echo "$COINS" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .denom_pub_hash')
echo -n "Revoking denomination ${rd} (to affect coin ${rc}) ..."
# Find all other coins, which will be suspended
export susp=$(echo "$coins" | jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]')
export susp=$(echo "$COINS" | jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]')
# Do the revocation
taler-exchange-offline -c $CONF \
revoke-denomination "${rd}" upload &> ${MY_TMP_DIR}/taler-exchange-offline-revoke.log
taler-exchange-offline \
-c $CONF \
revoke-denomination "${rd}" \
upload \
&> taler-exchange-offline-revoke.log
echo "DONE"
echo -n "Signing replacement keys ..."
sleep 1 # Give exchange time to create replacmenent key
# Re-sign replacement keys
taler-auditor-offline -c $CONF \
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
taler-auditor-offline \
-c $CONF \
download \
sign \
upload \
&> taler-auditor-offline-reinit.log
echo " DONE"
# Now we suspend the other coins, so later we will pay with the recouped coin
taler-wallet-cli --wallet-db=$WALLET_DB advanced suspend-coins "$susp"
taler-wallet-cli \
--wallet-db="$WALLET_DB" \
advanced \
suspend-coins "$susp"
# Update exchange /keys so recoup gets scheduled
taler-wallet-cli --wallet-db=$WALLET_DB exchanges update \
-f $EXCHANGE_URL
taler-wallet-cli \
--wallet-db="$WALLET_DB" \
exchanges \
update \
-f "$EXCHANGE_URL"
# Block until scheduled operations are done
taler-wallet-cli --wallet-db=$WALLET_DB run-until-done
taler-wallet-cli \
--wallet-db="$WALLET_DB"\
run-until-done
# Now we buy something, only the coins resulting from recouped will be
# Now we buy something, only the coins resulting from recoup will be
# used, as other ones are suspended
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api 'testPay' \
taler-wallet-cli \
--no-throttle \
--wallet-db="$WALLET_DB" \
api \
'testPay' \
"$(jq -n '
{
amount: "TESTKUDOS:1",
merchantBaseUrl: $MERCHANT_URL,
summary: "foo",
}' \
--arg MERCHANT_URL $MERCHANT_URL
--arg MERCHANT_URL "$MERCHANT_URL"
)"
taler-wallet-cli --wallet-db=$WALLET_DB run-until-done
taler-wallet-cli \
--wallet-db="$WALLET_DB" \
run-until-done
echo "Purchase with recoup'ed coin (via reserve) done"
@ -477,9 +150,6 @@ echo "Will refresh coin ${rrc} of denomination ${zombie_denom}"
# Find all other coins, which will be suspended
export susp=$(echo "$coins" | jq --arg rrc "$rrc" '[.coins[] | select(.coin_pub != $rrc) | .coin_pub]')
export rrc
export zombie_denom
# Travel into the future! (must match DURATION_WITHDRAW option)
export TIMETRAVEL="--timetravel=604800000000"
@ -510,8 +180,15 @@ do
done
echo "Refreshing coin $rrc"
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB advanced force-refresh "$rrc"
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB run-until-done
taler-wallet-cli \
"$TIMETRAVEL" \
--wallet-db="$WALLET_DB" \
advanced force-refresh \
"$rrc"
taler-wallet-cli \
"$TIMETRAVEL" \
--wallet-db="$WALLET_DB" \
run-until-done
# Update our list of the coins
export coins=$(taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB advanced dump-coins)
@ -534,29 +211,49 @@ export susp=$(echo "$coins" | jq --arg freshc "$freshc" '[.coins[] | select(.coi
# Do the revocation of freshc
echo "Revoking ${fresh_denom} (to affect coin ${freshc})"
taler-exchange-offline -c $CONF \
revoke-denomination "${fresh_denom}" upload &> ${MY_TMP_DIR}/taler-exchange-offline-revoke-2.log
taler-exchange-offline \
-c "$CONF" \
revoke-denomination \
"${fresh_denom}" \
upload &> taler-exchange-offline-revoke-2.log
sleep 1 # Give exchange time to create replacmenent key
# Re-sign replacement keys
taler-auditor-offline -c $CONF \
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
taler-auditor-offline \
-c "$CONF" \
download \
sign \
upload &> taler-auditor-offline.log
# Now we suspend the other coins, so later we will pay with the recouped coin
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB advanced suspend-coins "$susp"
taler-wallet-cli \
"$TIMETRAVEL" \
--wallet-db="$WALLET_DB" \
advanced \
suspend-coins "$susp"
# Update exchange /keys so recoup gets scheduled
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB exchanges update \
-f $EXCHANGE_URL
taler-wallet-cli \
"$TIMETRAVEL"\
--wallet-db="$WALLET_DB" \
exchanges update \
-f "$EXCHANGE_URL"
# Block until scheduled operations are done
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB run-until-done
taler-wallet-cli \
"$TIMETRAVEL" \
--wallet-db="$WALLET_DB" \
run-until-done
echo "Restarting merchant (so new keys are known)"
kill -TERM $MERCHANT_PID
taler-merchant-httpd -c $CONF -L INFO 2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
taler-merchant-httpd \
-c "$CONF" \
-L INFO \
2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
MERCHANT_PID=$!
# Wait for merchant to be again available
for n in `seq 1 50`
do
@ -580,7 +277,10 @@ taler-wallet-cli $TIMETRAVEL --no-throttle --wallet-db=$WALLET_DB api 'testPay'
}' \
--arg MERCHANT_URL $MERCHANT_URL
)"
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB run-until-done
taler-wallet-cli \
"$TIMETRAVEL" \
--wallet-db="$WALLET_DB" \
run-until-done
echo "Bought something with refresh-recouped coin"
@ -588,26 +288,24 @@ echo "Shutting down services"
exit_cleanup
# Where do we write the result?
export BASEDB=${1:-"revoke-basedb"}
# Dump database
echo "Dumping database"
echo "Dumping PostgreSQL database: ${BASEDB}.sql"
pg_dump -O $TARGET_DB | sed -e '/AS integer/d' > ${BASEDB}.sql
echo "Dumping libeufin database: ${TARGET_DB}-libeufin-*.sql"
cd $MY_TMP_DIR
sqlite3 ${TARGET_DB}-nexus.sqlite3 ".dump" > ${BASEDB}-libeufin-nexus.sql
sqlite3 ${TARGET_DB}-sandbox.sqlite3 ".dump" > ${BASEDB}-libeufin-sandbox.sql
echo "Dumping database ${BASEDB}.sql"
pg_dump -O "auditor-basedb" | sed -e '/AS integer/d' > "${BASEDB}.sql"
rm ${TARGET_DB}-sandbox.sqlite3 ${TARGET_DB}-nexus.sqlite3 # libeufin DB
cd $ORIGIN
echo $MASTER_PUB > ${BASEDB}.mpub
echo "Final clean up"
dropdb $TARGET_DB
# clean up
echo -n "Final clean up ..."
kill -TERM "$SETUP_PID"
wait
unset SETUP_PID
dropdb "auditor-basedb"
echo " DONE"
echo "====================================="
echo " Finished generation of $BASEDB "
echo "Finished generation of ${BASEDB}.sql"
echo "====================================="
exit 0

View File

@ -0,0 +1 @@
%I7qYÿ®ÜX˜2@–šò%'1†”ÂOàÔæJ³Ô¦‘

72
src/auditor/setup.sh Executable file
View File

@ -0,0 +1,72 @@
#!/bin/sh
# This file is in the public domain
# Script to be inlined into the main test scripts. Defines function 'setup()'
# which wraps around 'taler-unified-setup.sh' to launch GNU Taler services.
# Call setup() with the arguments to pass to 'taler-unified-setup'. setup()
# will then launch GNU Taler, wait for the process to be complete before
# returning. The script will also install an exit handler to ensure the GNU
# Taler processes are stopped when the shell exits.
set -eu
# Cleanup to run whenever we exit
function exit_cleanup()
{
if [ ! -z ${SETUP_PID+x} ]
then
echo "Killing taler-unified-setup ($SETUP_PID)" >&2
kill -TERM "$SETUP_PID"
wait
fi
}
# Install cleanup handler (except for kill -9)
trap exit_cleanup EXIT
function setup()
{
echo "Starting test system ..." >&2
# Create a named pipe in a temp directory we own.
FIFO_DIR=$(mktemp -d fifo-XXXXXX)
FIFO_OUT=$(echo "$FIFO_DIR/out")
mkfifo "$FIFO_OUT"
# Open pipe as FD 3 (RW) and FD 4 (RO)
exec 3<> "$FIFO_OUT" 4< "$FIFO_OUT"
rm -rf "$FIFO_DIR"
# We require '-W' for our termination logic to work.
taler-unified-setup.sh -W "$@" >&3 &
SETUP_PID=$!
# Close FD3
exec 3>&-
sed -u '/<<READY>>/ q' <&4
# Close FD4
exec 4>&-
echo "Test system ready" >&2
}
# Exit, with status code "skip" (no 'real' failure)
function exit_fail() {
echo "$@" >&2
exit 1
}
# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
echo "SKIPPING: $1"
exit 77
}
function get_payto_uri() {
export LIBEUFIN_SANDBOX_USERNAME="$1"
export LIBEUFIN_SANDBOX_PASSWORD="$2"
export LIBEUFIN_SANDBOX_URL="http://localhost:18082"
libeufin-cli sandbox demobank info --bank-account "$1" | jq --raw-output '.paytoUri'
}
function get_bankaccount_transactions() {
export LIBEUFIN_SANDBOX_USERNAME=$1
export LIBEUFIN_SANDBOX_PASSWORD=$2
export LIBEUFIN_SANDBOX_URL="http://localhost:18082"
libeufin-cli sandbox demobank list-transactions --bank-account $1
}

View File

@ -213,7 +213,7 @@ main (int argc,
? "Could not remove exchange from database: entry already absent\n"
: "Could not add exchange to database: entry already exists\n");
TALER_AUDITORDB_plugin_unload (adb);
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
}
TALER_AUDITORDB_plugin_unload (adb);

View File

@ -132,7 +132,7 @@ handle_mhd_completion_callback (void *cls,
/**
* Handle a "/version" request.
* Handle a "/config" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
@ -142,11 +142,11 @@ handle_mhd_completion_callback (void *cls,
* @return MHD result code
*/
static MHD_RESULT
handle_version (struct TAH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size)
handle_config (struct TAH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size)
{
static json_t *ver; /* we build the response only once, keep around for next query! */
@ -157,6 +157,8 @@ handle_version (struct TAH_RequestHandler *rh,
if (NULL == ver)
{
ver = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("name",
"taler-auditor"),
GNUNET_JSON_pack_string ("version",
AUDITOR_PROTOCOL_VERSION),
GNUNET_JSON_pack_string ("currency",
@ -207,9 +209,9 @@ handle_mhd_request (void *cls,
{ "/exchanges", MHD_HTTP_METHOD_GET, "application/json",
NULL, 0,
&TAH_EXCHANGES_handler, MHD_HTTP_OK },
{ "/version", MHD_HTTP_METHOD_GET, "application/json",
{ "/config", MHD_HTTP_METHOD_GET, "application/json",
NULL, 0,
&handle_version, MHD_HTTP_OK },
&handle_config, MHD_HTTP_OK },
/* Landing page, for now tells humans to go away
* (NOTE: ideally, the reverse proxy will respond with a nicer page) */
{ "/", MHD_HTTP_METHOD_GET, "text/plain",

View File

@ -605,6 +605,9 @@ main (int argc,
level,
NULL));
GNUNET_free (level);
/* suppress compiler warnings... */
GNUNET_assert (NULL != src_cfgfile);
GNUNET_assert (NULL != dst_cfgfile);
if (0 == strcmp (src_cfgfile,
dst_cfgfile))
{

View File

@ -83,7 +83,9 @@ optcheck "$@"
ARGS=("$@")
ARGS=(${ARGS[@]/$INF})
DIR=`mktemp -d reportXXXXXX`
DATE=`date +%F_%H:%M:%S`
DIR="report_$DATE"
mkdir $DIR
for n in aggregation coins deposits purses reserves
do
taler-helper-auditor-$n ${ARGS[*]} > ${DIR}/$n.json

View File

@ -172,9 +172,9 @@ coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
uint32_t i;
memcpy (&i,
coin_pub,
sizeof (i));
GNUNET_memcpy (&i,
coin_pub,
sizeof (i));
return i % MAX_COIN_HISTORIES;
}

View File

@ -674,12 +674,12 @@ hash_rc (const char *receiver_account,
size_t slen = strlen (receiver_account);
char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
memcpy (buf,
wtid,
sizeof (*wtid));
memcpy (&buf[sizeof (*wtid)],
receiver_account,
slen);
GNUNET_memcpy (buf,
wtid,
sizeof (*wtid));
GNUNET_memcpy (&buf[sizeof (*wtid)],
receiver_account,
slen);
GNUNET_CRYPTO_hash (buf,
sizeof (buf),
key);
@ -1483,10 +1483,10 @@ history_debit_cb (void *cls,
switch (dhr->http_status)
{
case MHD_HTTP_OK:
for (unsigned int i = 0; i<dhr->details.success.details_length; i++)
for (unsigned int i = 0; i<dhr->details.ok.details_length; i++)
{
const struct TALER_BANK_DebitDetails *dd
= &dhr->details.success.details[i];
= &dhr->details.ok.details[i];
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
GNUNET_TIME_timestamp2s (dd->execution_date),
@ -1504,9 +1504,9 @@ history_debit_cb (void *cls,
roi->details.execution_date = dd->execution_date;
roi->details.wtid = dd->wtid;
roi->details.credit_account_uri = (const char *) &roi[1];
memcpy (&roi[1],
dd->credit_account_uri,
slen);
GNUNET_memcpy (&roi[1],
dd->credit_account_uri,
slen);
if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (out_map,
&roi->subject_hash,
@ -1678,9 +1678,9 @@ reserve_in_cb (void *cls,
rii->details.execution_date = execution_date;
rii->details.reserve_pub = *reserve_pub;
rii->details.debit_account_uri = (const char *) &rii[1];
memcpy (&rii[1],
sender_account_details,
slen);
GNUNET_memcpy (&rii[1],
sender_account_details,
slen);
GNUNET_CRYPTO_hash (&wire_reference,
sizeof (uint64_t),
&rii->row_off_hash);
@ -1978,10 +1978,10 @@ history_credit_cb (void *cls,
switch (chr->http_status)
{
case MHD_HTTP_OK:
for (unsigned int i = 0; i<chr->details.success.details_length; i++)
for (unsigned int i = 0; i<chr->details.ok.details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd
= &chr->details.success.details[i];
= &chr->details.ok.details[i];
if (! analyze_credit (wa,
cd))

View File

@ -1,7 +1,7 @@
#!/bin/bash
#
# This file is part of TALER
# Copyright (C) 2014-2022 Taler Systems SA
# Copyright (C) 2014-2023 Taler Systems SA
#
# TALER is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
@ -49,17 +49,7 @@ VALGRIND=""
# history request.
LIBEUFIN_SETTLE_TIME=1
# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
echo "SKIPPING test: $1"
exit 77
}
# Exit, with error message (hard failure)
function exit_fail() {
echo "FAILING test: $1"
exit 1
}
. setup.sh
# Stop libeufin sandbox and nexus (if running)
function stop_libeufin()
@ -2002,12 +1992,12 @@ function check_with_database()
{
BASEDB=$1
CONF=$1.conf
ORIGIN=`pwd`
MY_TMP_DIR=`dirname $1`
ORIGIN=$(pwd)
MY_TMP_DIR=$(dirname $1)
echo "Running test suite with database $BASEDB using configuration $CONF"
MASTER_PRIV_FILE=${BASEDB}.mpriv
taler-config -f -c ${CONF} -s exchange-offline -o MASTER_PRIV_FILE -V ${MASTER_PRIV_FILE}
MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
MASTER_PUB=$(gnunet-ecc -p $MASTER_PRIV_FILE)
echo "MASTER PUB is ${MASTER_PUB} using file ${MASTER_PRIV_FILE}"
@ -2037,7 +2027,7 @@ function check_with_database()
# ####### Setup globals ######
# Postgres database to use
export DB=auditor-basedb
export DB="auditor-basedb"
# test required commands exist
echo "Testing for jq"
@ -2059,12 +2049,12 @@ INITDB_BIN=$(command -v initdb) || true
if [[ ! -z "$INITDB_BIN" ]]; then
echo " FOUND (in path) at" $INITDB_BIN
else
HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep postgres` || exit_skip " MISSING"
echo " FOUND at" `dirname $HAVE_INITDB`
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
HAVE_INITDB=$(find /usr -name "initdb" | head -1 2> /dev/null | grep postgres) || exit_skip " MISSING"
echo " FOUND at" $(dirname $HAVE_INITDB)
INITDB_BIN=$(echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1)
fi
POSTGRES_PATH=`dirname $INITDB_BIN`
MYDIR=`mktemp -d /tmp/taler-auditor-basedbXXXXXX`
POSTGRES_PATH=$(dirname $INITDB_BIN)
MYDIR=$(mktemp -d /tmp/taler-auditor-basedbXXXXXX)
echo "Using $MYDIR for logging and temporary data"
TMPDIR="$MYDIR/postgres/"
mkdir -p $TMPDIR
@ -2089,9 +2079,9 @@ PGHOST="$TMPDIR/sockets"
export PGHOST
echo "Generating fresh database at $MYDIR"
if faketime -f '-1 d' ./generate-auditor-basedb.sh $MYDIR/$DB
if faketime -f '-1 d' ./generate-auditor-basedb.sh "$MYDIR/$DB"
then
check_with_database $MYDIR/$DB
check_with_database "$MYDIR/$DB"
if test x$fail != x0
then
exit $fail

View File

@ -74,25 +74,25 @@ handle_admin_add_incoming_finished (void *cls,
const void *response)
{
struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
uint64_t row_id = UINT64_MAX;
struct GNUNET_TIME_Timestamp timestamp;
enum TALER_ErrorCode ec;
const json_t *j = response;
struct TALER_BANK_AdminAddIncomingResponse ir = {
.http_status = response_code,
.response = response
};
aai->job = NULL;
timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
switch (response_code)
{
case 0:
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id",
&row_id),
&ir.details.ok.serial_id),
GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp),
&ir.details.ok.timestamp),
GNUNET_JSON_spec_end ()
};
@ -102,42 +102,41 @@ handle_admin_add_incoming_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
ir.http_status = 0;
ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
ec = TALER_EC_NONE;
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says the password is invalid; we should
pass the JSON reply to the application */
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, maybe account really does not exist.
We should pass the JSON reply to the application */
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_CONFLICT:
/* Nothing to verify, we used the same wire subject
twice? */
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
default:
/* unexpected response code */
@ -145,15 +144,11 @@ handle_admin_add_incoming_finished (void *cls,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_JSON_get_error_code (j);
ir.ec = TALER_JSON_get_error_code (j);
break;
}
aai->cb (aai->cb_cls,
response_code,
ec,
row_id,
timestamp,
j);
&ir);
TALER_BANK_admin_add_incoming_cancel (aai);
}

View File

@ -131,8 +131,8 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
return GNUNET_SYSERR;
}
}
chr.details.success.details_length = len;
chr.details.success.details = cd;
chr.details.ok.details_length = len;
chr.details.ok.details = cd;
hh->hcb (hh->hcb_cls,
&chr);
}

View File

@ -133,8 +133,8 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
return GNUNET_SYSERR;
}
}
dhr.details.success.details_length = len;
dhr.details.success.details = dd;
dhr.details.ok.details_length = len;
dhr.details.ok.details = dd;
hh->hcb (hh->hcb_cls,
&dhr);
}

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015--2020 Taler Systems SA
Copyright (C) 2015--2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -99,12 +99,12 @@ TALER_BANK_prepare_transfer (
wp->account_len = htonl ((uint32_t) d_len);
wp->exchange_url_len = htonl ((uint32_t) u_len);
end = (char *) &wp[1];
memcpy (end,
destination_account_payto_uri,
d_len);
memcpy (end + d_len,
exchange_base_url,
u_len);
GNUNET_memcpy (end,
destination_account_payto_uri,
d_len);
GNUNET_memcpy (end + d_len,
exchange_base_url,
u_len);
*buf = (char *) wp;
}
@ -158,23 +158,24 @@ handle_transfer_finished (void *cls,
{
struct TALER_BANK_TransferHandle *th = cls;
const json_t *j = response;
uint64_t row_id = UINT64_MAX;
struct GNUNET_TIME_Timestamp timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
enum TALER_ErrorCode ec;
struct TALER_BANK_TransferResponse tr = {
.http_status = response_code,
.response = j
};
th->job = NULL;
switch (response_code)
{
case 0:
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id",
&row_id),
&tr.details.ok.row_id),
GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp),
&tr.details.ok.timestamp),
GNUNET_JSON_spec_end ()
};
@ -184,39 +185,38 @@ handle_transfer_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
tr.http_status = 0;
tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
ec = TALER_EC_NONE;
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j);
tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says our credentials are
invalid. We should pass the JSON reply to the application. */
ec = TALER_JSON_get_error_code (j);
tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, endpoint wrong -- could be user unknown */
ec = TALER_JSON_get_error_code (j);
tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_CONFLICT:
/* Nothing really to verify. Server says we used the same transfer request
UID before, but with different details. Should not happen if the user
properly used #TALER_BANK_prepare_transfer() and our PRNG is not
broken... */
ec = TALER_JSON_get_error_code (j);
tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
ec = TALER_JSON_get_error_code (j);
tr.ec = TALER_JSON_get_error_code (j);
break;
default:
/* unexpected response code */
@ -224,14 +224,11 @@ handle_transfer_finished (void *cls,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_JSON_get_error_code (j);
tr.ec = TALER_JSON_get_error_code (j);
break;
}
th->cb (th->cb_cls,
response_code,
ec,
row_id,
timestamp);
&tr);
TALER_BANK_transfer_cancel (th);
}

View File

@ -1389,9 +1389,9 @@ make_transfer (
if (NULL != timestamp)
*timestamp = t->date;
t->type = T_DEBIT;
memcpy (t->subject.debit.exchange_base_url,
exchange_base_url,
url_len);
GNUNET_memcpy (t->subject.debit.exchange_base_url,
exchange_base_url,
url_len);
t->subject.debit.wtid = *subject;
if (NULL == request_uid)
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,

View File

@ -179,10 +179,10 @@ credit_history_cb (void *cls,
global_ret = 0;
break;
case MHD_HTTP_OK:
for (unsigned int i = 0; i<reply->details.success.details_length; i++)
for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd =
&reply->details.success.details[i];
&reply->details.ok.details[i];
/* If credit/debit accounts were specified, use as a filter */
if ( (NULL != credit_account) &&
@ -279,10 +279,10 @@ debit_history_cb (void *cls,
global_ret = 0;
break;
case MHD_HTTP_OK:
for (unsigned int i = 0; i<reply->details.success.details_length; i++)
for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
const struct TALER_BANK_DebitDetails *dd =
&reply->details.success.details[i];
&reply->details.ok.details[i];
/* If credit/debit accounts were specified, use as a filter */
if ( (NULL != credit_account) &&
@ -357,34 +357,28 @@ execute_debit_history (void)
* execution.
*
* @param cls closure
* @param response_code HTTP status code
* @param ec taler error code
* @param row_id unique ID of the wire transfer in the bank's records
* @param timestamp when did the transaction go into effect
* @param tr response details
*/
static void
confirmation_cb (void *cls,
unsigned int response_code,
enum TALER_ErrorCode ec,
uint64_t row_id,
struct GNUNET_TIME_Timestamp timestamp)
const struct TALER_BANK_TransferResponse *tr)
{
(void) cls;
eh = NULL;
if (MHD_HTTP_OK != response_code)
if (MHD_HTTP_OK != tr->http_status)
{
fprintf (stderr,
"The wire transfer didn't execute correctly (%u/%d).\n",
response_code,
ec);
tr->http_status,
tr->ec);
GNUNET_SCHEDULER_shutdown ();
return;
}
fprintf (stdout,
"Wire transfer #%llu executed successfully at %s.\n",
(unsigned long long) row_id,
GNUNET_TIME_timestamp2s (timestamp));
(unsigned long long) tr->details.ok.row_id,
GNUNET_TIME_timestamp2s (tr->details.ok.timestamp));
global_ret = 0;
GNUNET_SCHEDULER_shutdown ();
}
@ -464,39 +458,29 @@ execute_wire_transfer (void)
* Function called with the result of the operation.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol)
* @param ec detailed error code
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
* @param timestamp timestamp when the transaction got settled at the bank.
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
* @param air response details
*/
static void
res_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id,
struct GNUNET_TIME_Timestamp timestamp,
const json_t *json)
const struct TALER_BANK_AdminAddIncomingResponse *air)
{
(void) cls;
(void) timestamp;
op = NULL;
switch (ec)
switch (air->http_status)
{
case TALER_EC_NONE:
case MHD_HTTP_OK:
global_ret = 0;
fprintf (stdout,
"%llu\n",
(unsigned long long) serial_id);
(unsigned long long) air->details.ok.serial_id);
break;
default:
fprintf (stderr,
"Operation failed with status code %u/%u\n",
(unsigned int) ec,
http_status);
if (NULL != json)
json_dumpf (json,
(unsigned int) air->ec,
air->http_status);
if (NULL != air->response)
json_dumpf (air->response,
stderr,
JSON_INDENT (2));
break;

View File

@ -1,4 +1,5 @@
#!/bin/bash
# This file is in the public domain.
set -eu

View File

@ -1,2 +1,3 @@
taler-bank-benchmark
taler-aggregator-benchmark
*.edited

View File

@ -15,6 +15,7 @@ bin_PROGRAMS = \
taler-bank-benchmark \
taler-exchange-benchmark
taler_aggregator_benchmark_SOURCES = \
taler-aggregator-benchmark.c
taler_aggregator_benchmark_LDADD = \
@ -64,6 +65,11 @@ taler_exchange_benchmark_LDADD = \
$(XLIB)
EXTRA_DIST = \
benchmark-common.conf \
benchmark-cs.conf \
benchmark-rsa.conf \
bank-benchmark-cs.conf \
bank-benchmark-rsa.conf \
coins-cs.conf \
coins-rsa.conf \
exchange_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv

View File

@ -1,128 +1,17 @@
# This file is in the public domain.
#
[paths]
# Persistent data storage for the testcase
# This value is a default for `taler_config_home'
taler_test_home = exchange_benchmark_home/
[taler]
# Currency supported by the exchange (can only be one)
currency = EUR
CURRENCY_ROUND_UNIT = EUR:0.01
[exchange]
# how long is one signkey valid?
signkey_duration = 4 weeks
signkey_legal_duration = 2 years
# how long do we provide to clients denomination and signing keys
# ahead of time?
# Keep it short so the test runs fast.
lookahead_sign = 12h
# HTTP port the exchange listens to
port = 8081
# Master public key used to sign the exchange's various keys
master_public_key = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# How to access our database
DB = postgres
# Base URL of the exchange. Must be set to a URL where the
# exchange (or the twister) is actually listening.
base_url = "http://localhost:8081/"
WIREWATCH_IDLE_SLEEP_INTERVAL = 500 ms
[exchange-offline]
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
[auditor]
BASE_URL = "http://localhost:8083/"
[exchangedb-postgres]
config = "postgres:///talercheck"
[benchmark-remote-exchange]
host = localhost
# Adjust $HOME to match remote target!
dir = $HOME/repos/taler/exchange/src/benchmark
[bank]
HTTP_PORT = 8082
SERVE = http
MAX_DEBT = EUR:100000000000.0
MAX_DEBT_BANK = EUR:1000000000000000.0
[benchmark]
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42?receiver-name=user42
@INLINE@ benchmark-common.conf
@INLINE@ coins-cs.conf
[exchange-account-2]
# What is the payto://-URL of the exchange (to generate wire response)
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange?receiver-name=Exchange"
enable_debit = YES
enable_credit = YES
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
[exchange-accountcredentials-2]
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
# Authentication information for basic authentication
WIRE_GATEWAY_AUTH_METHOD = "basic"
username = Exchange
password = x
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
USERNAME = Exchange
PASSWORD = x

View File

@ -1,133 +1,17 @@
# This file is in the public domain.
#
[paths]
# Persistent data storage for the testcase
# This value is a default for `taler_config_home'
taler_test_home = exchange_benchmark_home/
[taler]
# Currency supported by the exchange (can only be one)
currency = EUR
CURRENCY_ROUND_UNIT = EUR:0.01
[exchange]
# how long is one signkey valid?
signkey_duration = 4 weeks
signkey_legal_duration = 2 years
# how long do we provide to clients denomination and signing keys
# ahead of time?
# Keep it short so the test runs fast.
lookahead_sign = 12h
# HTTP port the exchange listens to
port = 8081
# Master public key used to sign the exchange's various keys
master_public_key = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# How to access our database
DB = postgres
# Base URL of the exchange. Must be set to a URL where the
# exchange (or the twister) is actually listening.
base_url = "http://localhost:8081/"
WIREWATCH_IDLE_SLEEP_INTERVAL = 1500 ms
[exchange-offline]
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
[auditor]
BASE_URL = "http://localhost:8083/"
[exchangedb-postgres]
config = "postgres://exchange:taler@192.168.42.42/exchange"
[benchmark-remote-exchange]
host = localhost
# Adjust $HOME to match remote target!
dir = $HOME/repos/taler/exchange/src/benchmark
[bank]
HTTP_PORT = 8082
SERVE = http
MAX_DEBT = EUR:100000000000.0
MAX_DEBT_BANK = EUR:1000000000000000.0
[benchmark]
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
@INLINE@ benchmark-common.conf
@INLINE@ coins-cs.conf
[exchange-account-2]
# What is the payto://-URL of the exchange (to generate wire response)
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange"
enable_debit = YES
enable_credit = YES
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange?receiver-name=Exchange"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
[exchange-accountcredentials-2]
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
# Authentication information for basic authentication
WIRE_GATEWAY_AUTH_METHOD = "basic"
username = Exchange
password = x
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
USERNAME = Exchange
PASSWORD = x

View File

@ -0,0 +1,89 @@
# This file is in the public domain.
[paths]
TALER_TEST_HOME=exchange_benchmark_home/
[taler]
CURRENCY=EUR
CURRENCY_ROUND_UNIT=EUR:0.01
[exchange]
AML_THRESHOLD=EUR:99999999
SIGNKEY_LEGAL_DURATION=2 years
PORT=8081
MASTER_PUBLIC_KEY=98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
DB=postgres
BASE_URL="http://localhost:8081/"
# Only set this option if you are actually running
# multiple aggregators!
# AGGREGATOR_SHARD_SIZE=67108864
WIREWATCH_IDLE_SLEEP_INTERVAL=5 ms
[exchangedb-postgres]
CONFIG="postgres:///talercheck"
[exchange-offline]
MASTER_PRIV_FILE=${TALER_DATA_HOME}/exchange/offline-keys/master.priv
[taler-exchange-secmod-rsa]
LOOKAHEAD_SIGN="1 d"
[taler-exchange-secmod-cs]
LOOKAHEAD_SIGN="1 d"
[taler-exchange-secmod-eddsa]
DURATION="2 d"
LOOKAHEAD_SIGN="1 d"
# account-2 is suitable for fakebank
[exchange-account-1]
PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
[exchange-accountcredentials-1]
WIRE_GATEWAY_AUTH_METHOD = none
WIRE_GATEWAY_URL = "http://localhost:8082/42/"
# account-2 is suitable for libeufin
[exchange-account-2]
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
PAYTO_URI = payto://iban/SANDBOXX/DE033310?receiver-name=Exchange+Company
[exchange-accountcredentials-2]
WIRE_GATEWAY_AUTH_METHOD = basic
USERNAME = exchange
PASSWORD = x
WIRE_GATEWAY_URL = "http://localhost:8082/facades/test-facade/taler-wire-gateway/"
# Trust local exchange for "EUR" currency
[merchant-exchange-benchmark]
EXCHANGE_BASE_URL = http://localhost:8081/
MASTER_KEY=98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# If currency does not match [TALER] section, the exchange
# will be ignored!
CURRENCY = EUR
[merchantdb-postgres]
CONFIG="postgres:///talercheck"
[auditordb-postgres]
CONFIG="postgres:///talercheck"
[syncdb-postgres]
CONFIG="postgres:///talercheck"
[bank]
HTTP_PORT=8082
SERVE=http
[libeufin-nexus]
DB_CONNECTION="jdbc:postgresql://localhost/talercheck?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
[libeufin-sandbox]
DB_CONNECTION="jdbc:postgresql://localhost/talercheck?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
[auditor]
BASE_URL="http://localhost:8083/"

View File

@ -1,58 +1,6 @@
# This file is in the public domain.
#
[paths]
# Persistent data storage for the testcase
# This value is a default for `taler_config_home'
TALER_TEST_HOME = exchange_benchmark_home/
[taler]
# Currency supported by the exchange (can only be one)
CURRENCY = EUR
CURRENCY_ROUND_UNIT = EUR:0.01
[exchange]
SIGNKEY_LEGAL_DURATION = 2 years
# HTTP port the exchange listens to
PORT = 8081
# Master public key used to sign the exchange's various keys
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# How to access our database
DB = postgres
# Base URL of the exchange. Must be set to a URL where the
# exchange (or the twister) is actually listening.
BASE_URL = "http://localhost:8081/"
AGGREGATOR_SHARD_SIZE = 67108864
#AGGREGATOR_SHARD_SIZE = 2147483648
WIREWATCH_IDLE_SLEEP_INTERVAL = 5 ms
[exchange-offline]
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
[auditor]
BASE_URL = "http://localhost:8083/"
[exchangedb-postgres]
CONFIG = "postgres:///talercheck"
[benchmark-remote-exchange]
HOST = localhost
# Adjust $HOME to match remote target!
DIR = $HOME/repos/taler/exchange/src/benchmark
[bank]
HTTP_PORT = 8082
SERVE = http
MAX_DEBT = EUR:100000000000.0
MAX_DEBT_BANK = EUR:1000000000000000.0
[benchmark]
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
@INLINE@ benchmark-common.conf
@INLINE@ coins-cs.conf
[exchange-account-test]
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
@ -66,61 +14,3 @@ WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
WIRE_GATEWAY_AUTH_METHOD = "basic"
USERNAME = Exchange
PASSWORD = x
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS

View File

@ -1,58 +1,6 @@
# This file is in the public domain.
#
[paths]
# Persistent data storage for the testcase
# This value is a default for `taler_config_home'
TALER_TEST_HOME = exchange_benchmark_home/
[taler]
# Currency supported by the exchange (can only be one)
CURRENCY = EUR
CURRENCY_ROUND_UNIT = EUR:0.01
[exchange]
SIGNKEY_LEGAL_DURATION = 2 years
# HTTP port the exchange listens to
PORT = 8081
# Master public key used to sign the exchange's various keys
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# How to access our database
DB = postgres
# Base URL of the exchange. Must be set to a URL where the
# exchange (or the twister) is actually listening.
BASE_URL = "http://localhost:8081/"
AGGREGATOR_SHARD_SIZE = 67108864
#AGGREGATOR_SHARD_SIZE = 2147483648
WIREWATCH_IDLE_SLEEP_INTERVAL = 5 ms
[exchange-offline]
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
[auditor]
BASE_URL = "http://localhost:8083/"
[exchangedb-postgres]
CONFIG = "postgres:///talercheck"
[benchmark-remote-exchange]
HOST = localhost
# Adjust $HOME to match remote target!
DIR = $HOME/repos/taler/exchange/src/benchmark
[bank]
HTTP_PORT = 8082
SERVE = http
MAX_DEBT = EUR:100000000000.0
MAX_DEBT_BANK = EUR:1000000000000000.0
[benchmark]
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
@INLINE@ benchmark-common.conf
@INLINE@ coins-rsa.conf
[exchange-account-test]
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
@ -66,66 +14,3 @@ WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
WIRE_GATEWAY_AUTH_METHOD = "basic"
USERNAME = Exchange
PASSWORD = x
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048

View File

@ -0,0 +1,58 @@
# This file is in the public domain.
#
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = CS

View File

@ -0,0 +1,63 @@
# This file is in the public domain.
#
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 2048

View File

@ -251,7 +251,7 @@ add_refund (const struct Merchant *m,
r.details.rtransaction_id = 42;
make_amount (0, 5000000, &r.details.refund_amount);
make_amount (0, 5, &r.details.refund_fee);
if (0 <=
if (0 >=
plugin->insert_refund (plugin->cls,
&r))
{
@ -307,8 +307,13 @@ add_deposit (const struct Merchant *m)
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 ();
do {
deposit.refund_deadline = random_time ();
deposit.wire_deadline = random_time ();
} while (GNUNET_TIME_timestamp_cmp (deposit.wire_deadline,
<,
deposit.refund_deadline));
make_amount (1, 0, &deposit.amount_with_fee);
make_amount (0, 5, &deposit.deposit_fee);
if (0 >=
@ -446,6 +451,9 @@ run (void *cls,
}
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
memset (&issue,
0,
sizeof (issue));
RANDOMIZE (&issue.signature);
issue.start
= start;
@ -478,6 +486,7 @@ run (void *cls,
TALER_DENOMINATION_RSA,
1024));
alg_values.cipher = TALER_DENOMINATION_RSA;
denom_pub.age_mask = issue.age_mask;
TALER_denom_pub_hash (&denom_pub,
&h_denom_pub);
make_amount (2, 0, &issue.value);
@ -497,7 +506,6 @@ run (void *cls,
return;
}
TALER_planchet_blinding_secret_create (&ps,
&alg_values,
&bks);

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2014-2021 Taler Systems SA
(C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as
@ -39,65 +39,16 @@
#include "taler_error_codes.h"
/* Error codes. */
enum BenchmarkError
{
MISSING_BANK_URL,
FAILED_TO_LAUNCH_BANK,
BAD_CLI_ARG,
BAD_CONFIG_FILE,
NO_CONFIG_FILE_GIVEN
};
/**
* What mode should the benchmark run in?
* Credentials to use for the benchmark.
*/
enum BenchmarkMode
{
/**
* Run as client against the bank.
*/
MODE_CLIENT = 1,
/**
* Run the bank.
*/
MODE_BANK = 2,
/**
* Run both, for a local benchmark.
*/
MODE_BOTH = 3,
};
/**
* Hold information regarding which bank has the exchange account.
*/
static const struct TALER_EXCHANGEDB_AccountInfo *exchange_bank_account;
/**
* Time snapshot taken right before executing the CMDs.
*/
static struct GNUNET_TIME_Absolute start_time;
/**
* Benchmark duration time taken right after the CMD interpreter
* returns.
*/
static struct GNUNET_TIME_Relative duration;
static struct TALER_TESTING_Credentials cred;
/**
* Array of all the commands the benchmark is running.
*/
static struct TALER_TESTING_Command *all_commands;
/**
* Dummy keepalive task.
*/
static struct GNUNET_SCHEDULER_Task *keepalive;
/**
* Name of our configuration file.
*/
@ -105,27 +56,14 @@ static char *cfg_filename;
/**
* Use the fakebank instead of LibEuFin.
* NOTE: LibEuFin not yet supported! Set
* to 0 once we do support it!
*/
static int use_fakebank = 1;
/**
* Number of taler-exchange-wirewatchers to launch.
*/
static unsigned int start_wirewatch;
static int use_fakebank;
/**
* Verbosity level.
*/
static unsigned int verbose;
/**
* Size of the transaction history the fakebank
* should keep in RAM.
*/
static unsigned long long history_size = 65536;
/**
* How many reserves we want to create per client.
*/
@ -141,6 +79,11 @@ static unsigned int howmany_clients = 1;
*/
static unsigned int howmany_threads;
/**
* How many wirewatch processes do we want to create.
*/
static unsigned int start_wirewatch;
/**
* Log level used during the run.
*/
@ -151,32 +94,17 @@ static char *loglev;
*/
static char *logfile;
/**
* Benchmarking mode (run as client, exchange, both) as string.
*/
static char *mode_str;
/**
* Benchmarking mode (run as client, bank, both).
*/
static enum BenchmarkMode mode;
/**
* Don't kill exchange/fakebank/wirewatch until
* requested by the user explicitly.
*/
static int linger;
/**
* Do not initialize or reset the database.
*/
static int incremental;
/**
* Configuration.
*/
static struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Section with the configuration data for the exchange
* bank account.
*/
static char *exchange_bank_section;
/**
* Currency used.
*/
@ -237,10 +165,10 @@ print_stats (void)
total = GNUNET_strdup (
GNUNET_STRINGS_relative_time_to_string (timings[i].total_duration,
GNUNET_YES));
true));
latency = GNUNET_strdup (
GNUNET_STRINGS_relative_time_to_string (timings[i].success_latency,
GNUNET_YES));
true));
fprintf (stderr,
"%s-%d took %s in total with %s for latency for %u executions (%u repeats)\n",
timings[i].prefix,
@ -270,38 +198,39 @@ run (void *cls,
(void) cls;
len = howmany_reserves + 2;
all_commands = GNUNET_malloc_large (len
all_commands = GNUNET_malloc_large ((1 + len)
* sizeof (struct TALER_TESTING_Command));
GNUNET_assert (NULL != all_commands);
all_commands[0]
= TALER_TESTING_cmd_get_exchange ("get-exchange",
cred.cfg,
NULL,
true,
true);
GNUNET_asprintf (&total_reserve_amount,
"%s:5",
currency);
for (unsigned int j = 0; j < howmany_reserves; j++)
{
char *create_reserve_label;
char *user_payto_uri;
// FIXME: vary user accounts more...
GNUNET_assert (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_string (cfg,
"benchmark",
"USER_PAYTO_URI",
&user_payto_uri));
GNUNET_asprintf (&create_reserve_label,
"createreserve-%u",
j);
all_commands[j]
// TODO: vary user accounts more...
all_commands[1 + j]
= TALER_TESTING_cmd_admin_add_incoming_retry (
TALER_TESTING_cmd_admin_add_incoming (add_label (
create_reserve_label),
total_reserve_amount,
exchange_bank_account->auth,
add_label (user_payto_uri)));
&cred.ba,
cred.user42_payto));
}
GNUNET_free (total_reserve_amount);
all_commands[howmany_reserves]
all_commands[1 + howmany_reserves]
= TALER_TESTING_cmd_stat (timings);
all_commands[howmany_reserves + 1]
all_commands[1 + howmany_reserves + 1]
= TALER_TESTING_cmd_end ();
TALER_TESTING_run2 (is,
all_commands,
@ -318,15 +247,11 @@ launch_clients (void)
enum GNUNET_GenericReturnValue result = GNUNET_OK;
pid_t cpids[howmany_clients];
start_time = GNUNET_TIME_absolute_get ();
if (1 == howmany_clients)
{
/* do everything in this process */
result = TALER_TESTING_setup (&run,
NULL,
cfg,
NULL,
GNUNET_NO);
result = TALER_TESTING_loop (&run,
NULL);
if (verbose)
print_stats ();
return result;
@ -340,11 +265,8 @@ launch_clients (void)
GNUNET_log_setup ("benchmark-worker",
NULL == loglev ? "INFO" : loglev,
logfile);
result = TALER_TESTING_setup (&run,
NULL,
cfg,
NULL,
GNUNET_NO);
result = TALER_TESTING_loop (&run,
NULL);
if (verbose)
print_stats ();
if (GNUNET_OK != result)
@ -393,78 +315,6 @@ again:
}
/**
* Stop the fakebank.
*
* @param cls fakebank handle
*/
static void
stop_fakebank (void *cls)
{
struct TALER_FAKEBANK_Handle *fakebank = cls;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Stopping fakebank\n");
TALER_FAKEBANK_stop (fakebank);
GNUNET_SCHEDULER_cancel (keepalive);
keepalive = NULL;
}
/**
* Dummy task that is never run.
*/
static void
never_task (void *cls)
{
(void) cls;
GNUNET_assert (0);
}
/**
* Start the fakebank.
*
* @param cls NULL
*/
static void
launch_fakebank (void *cls)
{
struct TALER_FAKEBANK_Handle *fakebank;
unsigned long long pnum;
(void) cls;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"bank",
"HTTP_PORT",
&pnum))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"bank",
"HTTP_PORT",
"must be valid port number");
return;
}
fakebank
= TALER_FAKEBANK_start2 ((uint16_t) pnum,
currency,
history_size,
howmany_threads);
if (NULL == fakebank)
{
GNUNET_break (0);
return;
}
keepalive
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
&never_task,
NULL);
GNUNET_SCHEDULER_add_shutdown (&stop_fakebank,
fakebank);
}
/**
* Run the benchmark in parallel in many (client) processes
* and summarize result.
@ -475,278 +325,94 @@ static enum GNUNET_GenericReturnValue
parallel_benchmark (void)
{
enum GNUNET_GenericReturnValue result = GNUNET_OK;
pid_t fakebank = -1;
struct GNUNET_OS_Process *bankd = NULL;
struct GNUNET_OS_Process *wirewatch[GNUNET_NZL (start_wirewatch)];
memset (wirewatch,
0,
sizeof (wirewatch));
if ( (MODE_BANK == mode) ||
(MODE_BOTH == mode) )
/* start exchange wirewatch */
for (unsigned int w = 0; w<start_wirewatch; w++)
{
if (use_fakebank)
{
unsigned long long pnum;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"bank",
"HTTP_PORT",
&pnum))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"bank",
"HTTP_PORT",
"must be valid port number");
return GNUNET_SYSERR;
}
/* start fakebank */
fakebank = fork ();
if (0 == fakebank)
{
GNUNET_log_setup ("benchmark-fakebank",
NULL == loglev ? "INFO" : loglev,
logfile);
GNUNET_SCHEDULER_run (&launch_fakebank,
NULL);
exit (0);
}
if (-1 == fakebank)
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"fork");
return GNUNET_SYSERR;
}
/* wait for fakebank to be ready */
{
char *bank_url;
int ret;
GNUNET_asprintf (&bank_url,
"http://localhost:%u/",
(unsigned int) (uint16_t) pnum);
ret = TALER_TESTING_wait_httpd_ready (bank_url);
GNUNET_free (bank_url);
if (0 != ret)
{
int wstatus;
kill (fakebank,
SIGTERM);
if (fakebank !=
waitpid (fakebank,
&wstatus,
0))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"waitpid");
}
fakebank = -1;
exit (ret);
}
}
}
else
wirewatch[w] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", cfg_filename,
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
if (NULL == wirewatch[w])
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"FIXME #7273: launching LibEuFin not yet supported\n");
bankd = NULL; // FIXME #7273
return GNUNET_SYSERR;
}
if (0 == incremental)
{
struct GNUNET_OS_Process *dbinit;
dbinit = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-dbinit",
"taler-exchange-dbinit",
"-c", cfg_filename,
"-r",
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
if (NULL == dbinit)
{
if (NULL != bankd)
{
GNUNET_OS_process_kill (bankd,
SIGTERM);
GNUNET_OS_process_destroy (bankd);
bankd = NULL;
}
return GNUNET_SYSERR;
}
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (dbinit));
GNUNET_OS_process_destroy (dbinit);
}
/* start exchange wirewatch */
for (unsigned int w = 0; w<start_wirewatch; w++)
{
wirewatch[w] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", cfg_filename,
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
if (NULL == wirewatch[w])
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to launch wirewatch, aborting benchmark\n");
for (unsigned int x = 0; x<w; x++)
{
GNUNET_break (0 ==
GNUNET_OS_process_kill (wirewatch[x],
SIGTERM));
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[x]));
GNUNET_OS_process_destroy (wirewatch[x]);
wirewatch[x] = NULL;
}
if (-1 != fakebank)
{
int wstatus;
kill (fakebank,
SIGTERM);
if (fakebank !=
waitpid (fakebank,
&wstatus,
0))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"waitpid");
}
fakebank = -1;
}
if (NULL != bankd)
{
GNUNET_OS_process_kill (bankd,
SIGTERM);
GNUNET_OS_process_destroy (bankd);
bankd = NULL;
}
return GNUNET_SYSERR;
}
}
}
if ( (MODE_CLIENT == mode) ||
(MODE_BOTH == mode) )
result = launch_clients ();
if ( (GNUNET_YES == linger) ||
(MODE_BANK == mode) )
{
printf ("Press ENTER to stop!\n");
if (MODE_BANK != mode)
duration = GNUNET_TIME_absolute_get_duration (start_time);
(void) getchar ();
if (MODE_BANK == mode)
duration = GNUNET_TIME_absolute_get_duration (start_time);
}
if ( (MODE_BANK == mode) ||
(MODE_BOTH == mode) )
{
/* Ensure wirewatch runs to completion! */
if (0 != start_wirewatch)
{
/* replace ONE of the wirewatchers with one that is in test-mode */
GNUNET_break (0 ==
GNUNET_OS_process_kill (wirewatch[0],
SIGTERM));
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[0]));
GNUNET_OS_process_destroy (wirewatch[0]);
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", cfg_filename,
"-t",
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
/* wait for it to finish! */
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[0]));
GNUNET_OS_process_destroy (wirewatch[0]);
wirewatch[0] = NULL;
/* Then stop the rest, which should basically also be finished */
for (unsigned int w = 1; w<start_wirewatch; w++)
"Failed to launch wirewatch, aborting benchmark\n");
for (unsigned int x = 0; x<w; x++)
{
GNUNET_break (0 ==
GNUNET_OS_process_kill (wirewatch[w],
GNUNET_OS_process_kill (wirewatch[x],
SIGTERM));
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[w]));
GNUNET_OS_process_destroy (wirewatch[w]);
GNUNET_OS_process_wait (wirewatch[x]));
GNUNET_OS_process_destroy (wirewatch[x]);
wirewatch[x] = NULL;
}
/* But be extra sure we did finish all shards by doing one more */
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Shard check phase\n");
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", cfg_filename,
"-t",
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
/* wait for it to finish! */
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[0]));
GNUNET_OS_process_destroy (wirewatch[0]);
wirewatch[0] = NULL;
}
/* Now stop the time, if this was the right mode */
if ( (GNUNET_YES != linger) &&
(MODE_BANK != mode) )
duration = GNUNET_TIME_absolute_get_duration (start_time);
/* stop fakebank */
if (-1 != fakebank)
{
int wstatus;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Telling fakebank to shut down\n");
kill (fakebank,
SIGTERM);
if (fakebank !=
waitpid (fakebank,
&wstatus,
0))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"waitpid");
}
else
{
if ( (! WIFEXITED (wstatus)) ||
(0 != WEXITSTATUS (wstatus)) )
{
GNUNET_break (0);
result = GNUNET_SYSERR;
}
}
fakebank = -1;
}
if (NULL != bankd)
{
GNUNET_OS_process_kill (bankd,
SIGTERM);
GNUNET_OS_process_destroy (bankd);
return GNUNET_SYSERR;
}
}
result = launch_clients ();
/* Ensure wirewatch runs to completion! */
if (0 != start_wirewatch)
{
/* replace ONE of the wirewatchers with one that is in test-mode */
GNUNET_break (0 ==
GNUNET_OS_process_kill (wirewatch[0],
SIGTERM));
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[0]));
GNUNET_OS_process_destroy (wirewatch[0]);
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", cfg_filename,
"-t",
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
/* wait for it to finish! */
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[0]));
GNUNET_OS_process_destroy (wirewatch[0]);
wirewatch[0] = NULL;
/* Then stop the rest, which should basically also be finished */
for (unsigned int w = 1; w<start_wirewatch; w++)
{
GNUNET_break (0 ==
GNUNET_OS_process_kill (wirewatch[w],
SIGTERM));
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[w]));
GNUNET_OS_process_destroy (wirewatch[w]);
}
/* But be extra sure we did finish all shards by doing one more */
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Shard check phase\n");
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", cfg_filename,
"-t",
(NULL != loglev) ? "-L" : NULL,
loglev,
NULL);
/* wait for it to finish! */
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (wirewatch[0]));
GNUNET_OS_process_destroy (wirewatch[0]);
wirewatch[0] = NULL;
}
return result;
}
@ -766,32 +432,17 @@ main (int argc,
struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_mandatory (
GNUNET_GETOPT_option_cfgfile (&cfg_filename)),
#if FIXME_SUPPORT_LIBEUFIN
GNUNET_GETOPT_option_flag ('f',
"fakebank",
"start a fakebank instead of the Python bank",
"we are using fakebank",
&use_fakebank),
#endif
GNUNET_GETOPT_option_help ("taler-bank benchmark"),
GNUNET_GETOPT_option_flag ('K',
"linger",
"linger around until key press",
&linger),
GNUNET_GETOPT_option_flag ('i',
"incremental",
"skip initializing and resetting the database",
&incremental),
GNUNET_GETOPT_option_string ('l',
"logfile",
"LF",
"will log to file LF",
&logfile),
GNUNET_GETOPT_option_loglevel (&loglev),
GNUNET_GETOPT_option_string ('m',
"mode",
"MODE",
"run as bank, client or both",
&mode_str),
GNUNET_GETOPT_option_uint ('p',
"worker-parallelism",
"NPROCS",
@ -807,11 +458,12 @@ main (int argc,
"NRESERVES",
"How many reserves per client we should create",
&howmany_reserves),
GNUNET_GETOPT_option_ulong ('s',
"size",
"HISTORY_SIZE",
"Maximum history size kept in memory by the fakebank",
&history_size),
GNUNET_GETOPT_option_string (
'u',
"exchange-account-section",
"SECTION",
"use exchange bank account configuration from the given SECTION",
&exchange_bank_section),
GNUNET_GETOPT_option_version (PACKAGE_VERSION " " VCS_VERSION),
GNUNET_GETOPT_option_verbose (&verbose),
GNUNET_GETOPT_option_uint ('w',
@ -821,6 +473,7 @@ main (int argc,
&start_wirewatch),
GNUNET_GETOPT_OPTION_END
};
struct GNUNET_TIME_Relative duration;
unsetenv ("XDG_DATA_HOME");
unsetenv ("XDG_CONFIG_HOME");
@ -833,35 +486,15 @@ main (int argc,
GNUNET_free (cfg_filename);
if (GNUNET_NO == result)
return 0;
return BAD_CLI_ARG;
return EXIT_INVALIDARGUMENT;
}
if (NULL == exchange_bank_section)
exchange_bank_section = "exchange-account-1";
if (NULL == loglev)
loglev = "INFO";
GNUNET_log_setup ("taler-bank-benchmark",
NULL == loglev ? "INFO" : loglev,
loglev,
logfile);
if (history_size < 10)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"History size too small, this can hardly work\n");
return BAD_CLI_ARG;
}
if (NULL == mode_str)
mode = MODE_BOTH;
else if (0 == strcasecmp (mode_str,
"bank"))
mode = MODE_BANK;
else if (0 == strcasecmp (mode_str,
"client"))
mode = MODE_CLIENT;
else if (0 == strcasecmp (mode_str,
"both"))
mode = MODE_BOTH;
else
{
TALER_LOG_ERROR ("Unknown mode given: '%s'\n",
mode_str);
GNUNET_free (cfg_filename);
return BAD_CONFIG_FILE;
}
if (NULL == cfg_filename)
cfg_filename = GNUNET_CONFIGURATION_default_filename ();
if (NULL == cfg_filename)
@ -877,7 +510,7 @@ main (int argc,
{
TALER_LOG_ERROR ("Could not parse configuration\n");
GNUNET_free (cfg_filename);
return BAD_CONFIG_FILE;
return EXIT_NOTCONFIGURED;
}
if (GNUNET_OK !=
TALER_config_get_currency (cfg,
@ -885,52 +518,30 @@ main (int argc,
{
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (cfg_filename);
return BAD_CONFIG_FILE;
}
if (MODE_BANK != mode)
{
if (howmany_clients > 10240)
{
TALER_LOG_ERROR ("-p option value given is too large\n");
return BAD_CLI_ARG;
}
if (0 == howmany_clients)
{
TALER_LOG_ERROR ("-p option value must not be zero\n");
GNUNET_free (cfg_filename);
return BAD_CLI_ARG;
}
return EXIT_NOTCONFIGURED;
}
if (GNUNET_OK !=
TALER_EXCHANGEDB_load_accounts (cfg,
TALER_EXCHANGEDB_ALO_AUTHDATA
| TALER_EXCHANGEDB_ALO_CREDIT))
TALER_TESTING_get_credentials (
cfg_filename,
exchange_bank_section,
use_fakebank
? TALER_TESTING_BS_FAKEBANK
: TALER_TESTING_BS_IBAN,
&cred))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Configuration fails to provide exchange bank details\n");
"Required bank credentials not given in configuration\n");
GNUNET_free (cfg_filename);
return BAD_CONFIG_FILE;
return EXIT_NOTCONFIGURED;
}
exchange_bank_account
= TALER_EXCHANGEDB_find_account_by_method ("x-taler-bank");
if (NULL == exchange_bank_account)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"No bank account for `x-taler-bank` given in configuration\n");
GNUNET_free (cfg_filename);
return BAD_CONFIG_FILE;
}
result = parallel_benchmark ();
TALER_EXCHANGEDB_unload_accounts ();
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (cfg_filename);
struct GNUNET_TIME_Absolute start_time;
if (MODE_BANK == mode)
{
/* If we're the bank, we're done now. No need to print results. */
return (GNUNET_OK == result) ? 0 : result;
start_time = GNUNET_TIME_absolute_get ();
result = parallel_benchmark ();
duration = GNUNET_TIME_absolute_get_duration (start_time);
}
if (GNUNET_OK == result)
@ -958,7 +569,7 @@ main (int argc,
tps);
}
fprintf (stdout,
"CPU time: sys %llu user %llu\n", \
"CPU time: sys %llu user %llu\n",
(unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000
+ usage.ru_stime.tv_usec),
(unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000
@ -969,5 +580,7 @@ main (int argc,
GNUNET_array_grow (labels,
label_len,
0);
GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (cfg_filename);
return (GNUNET_OK == result) ? 0 : result;
}

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,10 @@
[exchange-offline]
# Where do we store the offline master private key of the exchange?
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange-offline/master.priv
MASTER_PRIV_FILE = ${TALER_DATA_HOME}exchange-offline/master.priv
# Where do we store the TOFU key material?
SECM_TOFU_FILE = ${TALER_DATA_HOME}/exchange-offline/secm_tofus.pub
SECM_TOFU_FILE = ${TALER_DATA_HOME}exchange-offline/secm_tofus.pub
# Base32-encoded public key of the RSA helper.
# SECM_DENOM_PUBKEY =

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2020-2021 Taler Systems SA
Copyright (C) 2020-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -174,7 +174,7 @@ static struct DenominationAddRequest *dar_tail;
/**
* Handle to the exchange, used to request /keys.
*/
static struct TALER_EXCHANGE_Handle *exchange;
static struct TALER_EXCHANGE_GetKeysHandle *exchange;
/**
@ -219,7 +219,7 @@ do_shutdown (void *cls)
}
if (NULL != exchange)
{
TALER_EXCHANGE_disconnect (exchange);
TALER_EXCHANGE_get_keys_cancel (exchange);
exchange = NULL;
}
if (NULL != nxt)
@ -388,14 +388,15 @@ load_offline_key (int do_create)
* add operation result.
*
* @param cls closure with a `struct DenominationAddRequest`
* @param hr HTTP response data
* @param adr response data
*/
static void
denomination_add_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_AuditorAddDenominationResponse *adr)
{
struct DenominationAddRequest *dar = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -644,30 +645,24 @@ do_upload (char *const *args)
* a particular exchange and what keys the exchange is using.
*
* @param cls closure with the `char **` remaining args
* @param hr HTTP response data
* @param keys information about the various keys used
* by the exchange, NULL if /keys failed
* @param compat protocol compatibility information
* @param kr response data
* @param keys key data from the exchange
*/
static void
keys_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_EXCHANGE_Keys *keys,
enum TALER_EXCHANGE_VersionCompatibility compat)
const struct TALER_EXCHANGE_KeysResponse *kr,
struct TALER_EXCHANGE_Keys *keys)
{
char *const *args = cls;
(void) keys;
(void) compat;
switch (hr->http_status)
exchange = NULL;
switch (kr->hr.http_status)
{
case MHD_HTTP_OK:
if (! json_is_object (hr->reply))
if (NULL == kr->hr.reply)
{
GNUNET_break (0);
TALER_EXCHANGE_disconnect (exchange);
exchange = NULL;
test_shutdown ();
global_ret = EXIT_FAILURE;
return;
@ -676,11 +671,9 @@ keys_cb (
default:
fprintf (stderr,
"Failed to download keys: %s (HTTP status: %u/%u)\n",
hr->hint,
hr->http_status,
(unsigned int) hr->ec);
TALER_EXCHANGE_disconnect (exchange);
exchange = NULL;
kr->hr.hint,
kr->hr.http_status,
(unsigned int) kr->hr.ec);
test_shutdown ();
global_ret = EXIT_FAILURE;
return;
@ -689,7 +682,7 @@ keys_cb (
GNUNET_JSON_pack_string ("operation",
OP_INPUT_KEYS),
GNUNET_JSON_pack_object_incref ("arguments",
(json_t *) hr->reply));
(json_t *) kr->hr.reply));
if (NULL == args[0])
{
json_dumpf (in,
@ -698,9 +691,8 @@ keys_cb (
json_decref (in);
in = NULL;
}
TALER_EXCHANGE_disconnect (exchange);
exchange = NULL;
next (args);
TALER_EXCHANGE_keys_decref (keys);
}
@ -727,11 +719,11 @@ do_download (char *const *args)
global_ret = EXIT_NOTCONFIGURED;
return;
}
exchange = TALER_EXCHANGE_connect (ctx,
exchange_url,
&keys_cb,
(void *) args,
TALER_EXCHANGE_OPTION_END);
exchange = TALER_EXCHANGE_get_keys (ctx,
exchange_url,
NULL,
&keys_cb,
(void *) args);
GNUNET_free (exchange_url);
}
@ -956,11 +948,11 @@ do_show (char *const *args)
json_t *keys;
const char *err_name;
unsigned int err_line;
json_t *denomkeys;
const json_t *denomkeys;
struct TALER_MasterPublicKeyP mpub;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("denoms",
&denomkeys),
GNUNET_JSON_spec_array_const ("denoms",
&denomkeys),
GNUNET_JSON_spec_fixed_auto ("master_public_key",
&mpub),
GNUNET_JSON_spec_end ()
@ -1004,11 +996,9 @@ do_show (char *const *args)
{
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
GNUNET_JSON_parse_free (spec);
json_decref (keys);
/* do NOT consume input if next argument is '-' */
if ( (NULL != args[0]) &&
@ -1145,10 +1135,10 @@ do_sign (char *const *args)
const char *err_name;
unsigned int err_line;
struct TALER_MasterPublicKeyP mpub;
json_t *denomkeys;
const json_t *denomkeys;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("denoms",
&denomkeys),
GNUNET_JSON_spec_array_const ("denoms",
&denomkeys),
GNUNET_JSON_spec_fixed_auto ("master_public_key",
&mpub),
GNUNET_JSON_spec_end ()
@ -1203,11 +1193,9 @@ do_sign (char *const *args)
{
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
GNUNET_JSON_parse_free (spec);
json_decref (keys);
next (args);
}

View File

@ -24,6 +24,8 @@
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
#include "taler_extensions.h"
#include <regex.h>
/**
* Name of the input for the 'sign' and 'show' operation.
@ -1119,14 +1121,15 @@ load_offline_key (int do_create)
* Function called with information about the post revocation operation result.
*
* @param cls closure with a `struct DenomRevocationRequest`
* @param hr HTTP response data
* @param dr response data
*/
static void
denom_revocation_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr)
{
struct DenomRevocationRequest *drr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1208,14 +1211,15 @@ upload_denom_revocation (const char *exchange_url,
* Function called with information about the post revocation operation result.
*
* @param cls closure with a `struct SignkeyRevocationRequest`
* @param hr HTTP response data
* @param sr response data
*/
static void
signkey_revocation_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr)
{
struct SignkeyRevocationRequest *srr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1297,13 +1301,15 @@ upload_signkey_revocation (const char *exchange_url,
* Function called with information about the post auditor add operation result.
*
* @param cls closure with a `struct AuditorAddRequest`
* @param hr HTTP response data
* @param mer response data
*/
static void
auditor_add_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
auditor_add_cb (
void *cls,
const struct TALER_EXCHANGE_ManagementAuditorEnableResponse *mer)
{
struct AuditorAddRequest *aar = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1397,13 +1403,15 @@ upload_auditor_add (const char *exchange_url,
* Function called with information about the post auditor del operation result.
*
* @param cls closure with a `struct AuditorDelRequest`
* @param hr HTTP response data
* @param mdr response data
*/
static void
auditor_del_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct
TALER_EXCHANGE_ManagementAuditorDisableResponse *mdr)
{
struct AuditorDelRequest *adr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1489,13 +1497,14 @@ upload_auditor_del (const char *exchange_url,
* Function called with information about the post wire add operation result.
*
* @param cls closure with a `struct WireAddRequest`
* @param hr HTTP response data
* @param wer response data
*/
static void
wire_add_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
{
struct WireAddRequest *war = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1533,10 +1542,21 @@ upload_wire_add (const char *exchange_url,
struct GNUNET_TIME_Timestamp start_time;
struct WireAddRequest *war;
const char *err_name;
const char *conversion_url = NULL;
const json_t *debit_restrictions;
const json_t *credit_restrictions;
unsigned int err_line;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("payto_uri",
&payto_uri),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("conversion_url",
&conversion_url),
NULL),
GNUNET_JSON_spec_array_const ("debit_restrictions",
&debit_restrictions),
GNUNET_JSON_spec_array_const ("credit_restrictions",
&credit_restrictions),
GNUNET_JSON_spec_timestamp ("validity_start",
&start_time),
GNUNET_JSON_spec_fixed_auto ("master_sig_add",
@ -1599,6 +1619,9 @@ upload_wire_add (const char *exchange_url,
TALER_EXCHANGE_management_enable_wire (ctx,
exchange_url,
payto_uri,
conversion_url,
debit_restrictions,
credit_restrictions,
start_time,
&master_sig_add,
&master_sig_wire,
@ -1614,13 +1637,14 @@ upload_wire_add (const char *exchange_url,
* Function called with information about the post wire del operation result.
*
* @param cls closure with a `struct WireDelRequest`
* @param hr HTTP response data
* @param wdres response data
*/
static void
wire_del_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres)
{
struct WireDelRequest *wdr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1706,14 +1730,15 @@ upload_wire_del (const char *exchange_url,
* Function called with information about the post wire fee operation result.
*
* @param cls closure with a `struct WireFeeRequest`
* @param hr HTTP response data
* @param swr response data
*/
static void
wire_fee_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementSetWireFeeResponse *swr)
{
struct WireFeeRequest *wfr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1811,14 +1836,15 @@ upload_wire_fee (const char *exchange_url,
* Function called with information about the post global fee operation result.
*
* @param cls closure with a `struct WireFeeRequest`
* @param hr HTTP response data
* @param gr response data
*/
static void
global_fee_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse *gr)
{
struct GlobalFeeRequest *gfr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -1927,14 +1953,15 @@ upload_global_fee (const char *exchange_url,
* Function called with information about the drain profits operation.
*
* @param cls closure with a `struct DrainProfitsRequest`
* @param hr HTTP response data
* @param mdr response data
*/
static void
drain_profits_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementDrainResponse *mdr)
{
struct DrainProfitsRequest *dpr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -2033,14 +2060,15 @@ upload_drain (const char *exchange_url,
* Function called with information about the post upload keys operation result.
*
* @param cls closure with a `struct UploadKeysRequest`
* @param hr HTTP response data
* @param mr response data
*/
static void
keys_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr)
{
struct UploadKeysRequest *ukr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -2076,13 +2104,13 @@ upload_keys (const char *exchange_url,
struct UploadKeysRequest *ukr;
const char *err_name;
unsigned int err_line;
json_t *denom_sigs;
json_t *signkey_sigs;
const json_t *denom_sigs;
const json_t *signkey_sigs;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("denom_sigs",
&denom_sigs),
GNUNET_JSON_spec_json ("signkey_sigs",
&signkey_sigs),
GNUNET_JSON_spec_array_const ("denom_sigs",
&denom_sigs),
GNUNET_JSON_spec_array_const ("signkey_sigs",
&signkey_sigs),
GNUNET_JSON_spec_end ()
};
bool ok = true;
@ -2198,7 +2226,6 @@ upload_keys (const char *exchange_url,
}
GNUNET_free (pkd.sign_sigs);
GNUNET_free (pkd.denom_sigs);
GNUNET_JSON_parse_free (spec);
}
@ -2206,14 +2233,15 @@ upload_keys (const char *exchange_url,
* Function called with information about the post upload extensions operation result.
*
* @param cls closure with a `struct UploadExtensionsRequest`
* @param hr HTTP response data
* @param er response data
*/
static void
extensions_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er)
{
struct UploadExtensionsRequest *uer = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -2245,13 +2273,13 @@ upload_extensions (const char *exchange_url,
size_t idx,
const json_t *value)
{
json_t *extensions;
const json_t *extensions;
struct TALER_MasterSignatureP sig;
const char *err_name;
unsigned int err_line;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("extensions",
&extensions),
GNUNET_JSON_spec_object_const ("extensions",
&extensions),
GNUNET_JSON_spec_fixed_auto ("extensions_sig",
&sig),
GNUNET_JSON_spec_end ()
@ -2282,9 +2310,9 @@ upload_extensions (const char *exchange_url,
struct TALER_ExtensionManifestsHashP h_manifests;
if (GNUNET_OK !=
TALER_JSON_extensions_manifests_hash (extensions, &h_manifests))
TALER_JSON_extensions_manifests_hash (extensions,
&h_manifests))
{
GNUNET_JSON_parse_free (spec);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"couldn't hash extensions' manifests\n");
global_ret = EXIT_FAILURE;
@ -2301,7 +2329,6 @@ upload_extensions (const char *exchange_url,
&master_pub,
&sig))
{
GNUNET_JSON_parse_free (spec);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"invalid signature for extensions\n");
global_ret = EXIT_FAILURE;
@ -2316,8 +2343,9 @@ upload_extensions (const char *exchange_url,
.extensions = extensions,
.extensions_sig = sig,
};
struct UploadExtensionsRequest *uer = GNUNET_new (struct
UploadExtensionsRequest);
struct UploadExtensionsRequest *uer
= GNUNET_new (struct UploadExtensionsRequest);
uer->idx = idx;
uer->h = TALER_EXCHANGE_management_post_extensions (
ctx,
@ -2329,7 +2357,6 @@ upload_extensions (const char *exchange_url,
uer_tail,
uer);
}
GNUNET_JSON_parse_free (spec);
}
@ -2337,14 +2364,15 @@ upload_extensions (const char *exchange_url,
* Function called with information about the add partner operation.
*
* @param cls closure with a `struct PartnerAddRequest`
* @param hr HTTP response data
* @param apr response data
*/
static void
add_partner_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementAddPartnerResponse *apr)
{
struct PartnerAddRequest *par = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -2447,14 +2475,15 @@ add_partner (const char *exchange_url,
* Function called with information about the AML officer update operation.
*
* @param cls closure with a `struct AmlStaffRequest`
* @param hr HTTP response data
* @param ar response data
*/
static void
update_aml_officer_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
{
struct AmlStaffRequest *asr = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
@ -2949,6 +2978,96 @@ do_del_auditor (char *const *args)
}
/**
* Parse account restriction.
*
* @param args the array of command-line arguments to process next
* @param[in,out] restrictions JSON array to update
* @return -1 on error, otherwise number of arguments from @a args that were used
*/
static int
parse_restriction (char *const *args,
json_t *restrictions)
{
if (NULL == args[0])
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Restriction TYPE argument missing\n");
return -1;
}
if (0 == strcmp (args[0],
"deny"))
{
GNUNET_assert (0 ==
json_array_append_new (
restrictions,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("type",
"deny"))));
return 1;
}
if (0 == strcmp (args[0],
"regex"))
{
json_t *i18n;
json_error_t err;
if ( (NULL == args[1]) ||
(NULL == args[2]) ||
(NULL == args[3]) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
return -1;
}
{
regex_t ex;
if (0 != regcomp (&ex,
args[1],
REG_NOSUB | REG_EXTENDED))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid regular expression `%s'\n",
args[1]);
return -1;
}
regfree (&ex);
}
i18n = json_loads (args[3],
JSON_REJECT_DUPLICATES,
&err);
if (NULL == i18n)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid JSON for restriction of type `regex': `%s` at %d\n",
args[3],
err.position);
return -1;
}
GNUNET_assert (0 ==
json_array_append_new (
restrictions,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("type",
"regex"),
GNUNET_JSON_pack_string ("regex",
args[1]),
GNUNET_JSON_pack_string ("human_hint",
args[2]),
GNUNET_JSON_pack_object_steal ("human_hint_i18n",
i18n)
)));
return 4;
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Restriction TYPE `%s' unsupported\n",
args[0]);
return -1;
}
/**
* Add wire account.
*
@ -2961,6 +3080,10 @@ do_add_wire (char *const *args)
struct TALER_MasterSignatureP master_sig_add;
struct TALER_MasterSignatureP master_sig_wire;
struct GNUNET_TIME_Timestamp now;
const char *conversion_url = NULL;
json_t *debit_restrictions;
json_t *credit_restrictions;
unsigned int num_args = 1;
if (NULL != in)
{
@ -3011,24 +3134,101 @@ do_add_wire (char *const *args)
}
GNUNET_free (wire_method);
}
debit_restrictions = json_array ();
GNUNET_assert (NULL != debit_restrictions);
credit_restrictions = json_array ();
GNUNET_assert (NULL != credit_restrictions);
while (NULL != args[num_args])
{
if (0 == strcmp (args[num_args],
"conversion-url"))
{
num_args++;
conversion_url = args[num_args];
if (NULL == conversion_url)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"'conversion-url' requires an argument\n");
global_ret = EXIT_INVALIDARGUMENT;
test_shutdown ();
json_decref (debit_restrictions);
json_decref (credit_restrictions);
return;
}
num_args++;
continue;
}
if (0 == strcmp (args[num_args],
"credit-restriction"))
{
int iret;
num_args++;
iret = parse_restriction (&args[num_args],
credit_restrictions);
if (iret <= 0)
{
global_ret = EXIT_INVALIDARGUMENT;
test_shutdown ();
json_decref (debit_restrictions);
json_decref (credit_restrictions);
return;
}
num_args += iret;
continue;
}
if (0 == strcmp (args[num_args],
"debit-restriction"))
{
int iret;
num_args++;
iret = parse_restriction (&args[num_args],
debit_restrictions);
if (iret <= 0)
{
global_ret = EXIT_INVALIDARGUMENT;
test_shutdown ();
json_decref (debit_restrictions);
json_decref (credit_restrictions);
return;
}
num_args += iret;
continue;
}
break;
}
TALER_exchange_offline_wire_add_sign (args[0],
conversion_url,
debit_restrictions,
credit_restrictions,
now,
&master_priv,
&master_sig_add);
TALER_exchange_wire_signature_make (args[0],
conversion_url,
debit_restrictions,
credit_restrictions,
&master_priv,
&master_sig_wire);
output_operation (OP_ENABLE_WIRE,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri",
args[0]),
GNUNET_JSON_pack_array_steal ("debit_restrictions",
debit_restrictions),
GNUNET_JSON_pack_array_steal ("credit_restrictions",
credit_restrictions),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("conversion_url",
conversion_url)),
GNUNET_JSON_pack_timestamp ("validity_start",
now),
GNUNET_JSON_pack_data_auto ("master_sig_add",
&master_sig_add),
GNUNET_JSON_pack_data_auto ("master_sig_wire",
&master_sig_wire)));
next (args + 1);
next (args + num_args);
}
@ -3198,14 +3398,28 @@ do_set_global_fee (char *const *args)
(NULL == args[3]) ||
(NULL == args[4]) ||
(NULL == args[5]) ||
(NULL == args[6]) ||
( (1 != sscanf (args[0],
"%u%c",
&year,
&dummy)) &&
(0 != strcasecmp ("now",
args[0])) ) ||
(GNUNET_OK !=
(NULL == args[6]) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if ( (1 != sscanf (args[0],
"%u%c",
&year,
&dummy)) &&
(0 != strcasecmp ("now",
args[0])) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid YEAR given for 'global-fee' subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if ( (GNUNET_OK !=
TALER_string_to_amount (args[1],
&fees.history)) ||
(GNUNET_OK !=
@ -3213,20 +3427,34 @@ do_set_global_fee (char *const *args)
&fees.account)) ||
(GNUNET_OK !=
TALER_string_to_amount (args[3],
&fees.purse)) ||
(GNUNET_OK !=
&fees.purse)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid amount given for 'global-fee' subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if ( (GNUNET_OK !=
GNUNET_STRINGS_fancy_time_to_relative (args[4],
&purse_timeout)) ||
(GNUNET_OK !=
GNUNET_STRINGS_fancy_time_to_relative (args[5],
&history_expiration)) ||
(1 != sscanf (args[6],
"%u%c",
&purse_account_limit,
&dummy)) )
&history_expiration)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
"Invalid delay given for 'global-fee' subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if (1 != sscanf (args[6],
"%u%c",
&purse_account_limit,
&dummy))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid purse account limit given for 'global-fee' subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
@ -3643,18 +3871,15 @@ enable_aml_staff (char *const *args)
* whether there are subsequent commands).
*
* @param cls closure with the `char **` remaining args
* @param hr HTTP response data
* @param keys information about the various keys used
* by the exchange, NULL if /management/keys failed
* @param mgr response data
*/
static void
download_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_EXCHANGE_FutureKeys *keys)
const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr)
{
char *const *args = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
(void) keys;
mgkh = NULL;
switch (hr->http_status)
{
@ -4258,15 +4483,15 @@ do_show (char *const *args)
json_t *keys;
const char *err_name;
unsigned int err_line;
json_t *denomkeys;
json_t *signkeys;
const json_t *denomkeys;
const json_t *signkeys;
struct TALER_MasterPublicKeyP mpub;
struct TALER_SecurityModulePublicKeySetP secmset;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("future_denoms",
&denomkeys),
GNUNET_JSON_spec_json ("future_signkeys",
&signkeys),
GNUNET_JSON_spec_array_const ("future_denoms",
&denomkeys),
GNUNET_JSON_spec_array_const ("future_signkeys",
&signkeys),
GNUNET_JSON_spec_fixed_auto ("master_pub",
&mpub),
GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
@ -4311,7 +4536,6 @@ do_show (char *const *args)
"Fatal: exchange uses different master key!\n");
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
@ -4320,7 +4544,6 @@ do_show (char *const *args)
{
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
@ -4334,12 +4557,10 @@ do_show (char *const *args)
{
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
json_decref (keys);
GNUNET_JSON_parse_free (spec);
next (args);
}
@ -4659,15 +4880,15 @@ do_sign (char *const *args)
json_t *keys;
const char *err_name;
unsigned int err_line;
json_t *denomkeys;
json_t *signkeys;
const json_t *denomkeys;
const json_t *signkeys;
struct TALER_MasterPublicKeyP mpub;
struct TALER_SecurityModulePublicKeySetP secmset;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("future_denoms",
&denomkeys),
GNUNET_JSON_spec_json ("future_signkeys",
&signkeys),
GNUNET_JSON_spec_array_const ("future_denoms",
&denomkeys),
GNUNET_JSON_spec_array_const ("future_signkeys",
&signkeys),
GNUNET_JSON_spec_fixed_auto ("master_pub",
&mpub),
GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
@ -4714,7 +4935,6 @@ do_sign (char *const *args)
"Fatal: exchange uses different master key!\n");
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
@ -4725,7 +4945,6 @@ do_sign (char *const *args)
"Fatal: security module keys changed!\n");
global_ret = EXIT_FAILURE;
test_shutdown ();
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
@ -4749,7 +4968,6 @@ do_sign (char *const *args)
test_shutdown ();
json_decref (signkey_sig_array);
json_decref (denomkey_sig_array);
GNUNET_JSON_parse_free (spec);
json_decref (keys);
return;
}
@ -4761,7 +4979,6 @@ do_sign (char *const *args)
GNUNET_JSON_pack_array_steal ("signkey_sigs",
signkey_sig_array)));
}
GNUNET_JSON_parse_free (spec);
json_decref (keys);
next (args);
}
@ -5045,7 +5262,7 @@ work (void *cls)
{
.name = "enable-account",
.help =
"enable wire account of the exchange (payto-URI must be given as argument)",
"enable wire account of the exchange (payto-URI must be given as argument; for optional argument see man page)",
.cb = &do_add_wire
},
{

View File

@ -15,6 +15,8 @@ pkgcfg_DATA = \
exchange.conf
# Programs
bin_SCRIPTS = \
taler-exchange-kyc-aml-pep-trigger.sh
bin_PROGRAMS = \
taler-exchange-aggregator \
@ -131,6 +133,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \
taler-exchange-httpd_age-withdraw_reveal.c taler-exchange-httpd_age-withdraw_reveal.h \
taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \
taler-exchange-httpd_common_kyc.c taler-exchange-httpd_common_kyc.h \
taler-exchange-httpd_config.c taler-exchange-httpd_config.h \
taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \
taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \
@ -181,7 +184,6 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
taler_exchange_httpd_LDADD = \
@ -227,4 +229,5 @@ EXTRA_DIST = \
test_taler_exchange_httpd.get \
test_taler_exchange_httpd.post \
exchange.conf \
$(bin_SCRIPTS) \
$(check_SCRIPTS)

View File

@ -6,10 +6,23 @@
# This must be adjusted to your actual installation.
# MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# Must be set to the threshold above which transactions
# are flagged for AML review.
# AML_THRESHOLD =
# Specifies a program (binary) to run on KYC attribute data to decide
# whether we should immediately flag an account for AML review.
# The KYC attribute data will be passed on standard-input.
# Return non-zero to trigger AML review of the new user.
KYC_AML_TRIGGER = true
# Attribute encryption key for storing attributes encrypted
# in the database. Should be a high-entropy nonce.
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
# Set to NO to disable rewards.
ENABLE_REWARDS = YES
# How long do we allow /keys to be cached at most? The actual
# limit is the minimum of this value and the first expected
# significant change in /keys based on the expiration times.

View File

@ -303,17 +303,17 @@ parse_aggregator_config (void)
(TALER_amount_is_zero (&currency_round_unit)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Need non-zero amount in section `TALER' under `CURRENCY_ROUND_UNIT'\n");
"Need non-zero amount in section `taler' under `CURRENCY_ROUND_UNIT'\n");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_config_get_amount (cfg,
"taler",
"exchange",
"AML_THRESHOLD",
&aml_threshold))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Need amount in section `TALER' under `AML_THRESHOLD'\n");
"Need amount in section `exchange' under `AML_THRESHOLD'\n");
return GNUNET_SYSERR;
}

View File

@ -312,10 +312,10 @@ expired_reserve_cb (void *cls,
memset (&wtid,
0,
sizeof (wtid));
memcpy (&wtid,
reserve_pub,
GNUNET_MIN (sizeof (wtid),
sizeof (*reserve_pub)));
GNUNET_memcpy (&wtid,
reserve_pub,
GNUNET_MIN (sizeof (wtid),
sizeof (*reserve_pub)));
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
reserve_pub,
now,

View File

@ -30,6 +30,8 @@
#include "taler_kyclogic_lib.h"
#include "taler_templating_lib.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_age-withdraw.h"
#include "taler-exchange-httpd_age-withdraw_reveal.h"
#include "taler-exchange-httpd_aml-decision.h"
#include "taler-exchange-httpd_auditors.h"
#include "taler-exchange-httpd_batch-deposit.h"
@ -68,7 +70,6 @@
#include "taler-exchange-httpd_reserves_status.h"
#include "taler-exchange-httpd_terms.h"
#include "taler-exchange-httpd_transfers_get.h"
#include "taler-exchange-httpd_wire.h"
#include "taler-exchange-httpd_withdraw.h"
#include "taler_exchangedb_lib.h"
#include "taler_exchangedb_plugin.h"
@ -153,6 +154,16 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/
char *TEH_currency;
/**
* Name of the KYC-AML-trigger evaluation binary.
*/
char *TEH_kyc_aml_trigger;
/**
* Option set to #GNUNET_YES if rewards are enabled.
*/
int TEH_enable_rewards;
/**
* What is the largest amount we allow a peer to
* merge into a reserve before always triggering
@ -542,7 +553,6 @@ handle_get_aml (struct TEH_RequestContext *rc,
TALER_EC_GENERIC_DB_FETCH_FAILED,
NULL);
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
@ -562,6 +572,46 @@ handle_get_aml (struct TEH_RequestContext *rc,
}
/**
* Handle a "/age-withdraw/$ACH/reveal" POST request. Parses the "ACH"
* hash of the commitment from a previous call to
* /reserves/$reserve_pub/age-withdraw
*
* @param rc request context
* @param root uploaded JSON data
* @param args array of additional options
* @return MHD result code
*/
static MHD_RESULT
handle_post_age_withdraw (struct TEH_RequestContext *rc,
const json_t *root,
const char *const args[2])
{
struct TALER_AgeWithdrawCommitmentHashP ach;
if (0 != strcmp ("reveal", args[1]))
return r404 (rc->connection,
args[1]);
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&ach,
sizeof (ach)))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
return TEH_handler_age_withdraw_reveal (rc,
&ach,
root);
}
/**
* Signature of functions that handle operations on reserves.
*
@ -608,6 +658,10 @@ handle_post_reserves (struct TEH_RequestContext *rc,
.op = "batch-withdraw",
.handler = &TEH_handler_batch_withdraw
},
{
.op = "age-withdraw",
.handler = &TEH_handler_age_withdraw
},
{
.op = "withdraw",
.handler = &TEH_handler_withdraw
@ -932,9 +986,9 @@ proceed_with_handler (struct TEH_RequestContext *rc,
/* Parse command-line arguments */
/* make a copy of 'url' because 'strtok_r()' will modify */
memcpy (d,
url,
ulen);
GNUNET_memcpy (d,
url,
ulen);
i = 0;
args[i++] = strtok_r (d, "/", &sp);
while ( (NULL != args[i - 1]) &&
@ -1445,6 +1499,12 @@ handle_mhd_request (void *cls,
.handler.post = &handle_post_reserves,
.nargs = 2
},
{
.url = "age-withdraw",
.method = MHD_HTTP_METHOD_POST,
.handler.post = &handle_post_age_withdraw,
.nargs = 2
},
{
.url = "reserves-attest",
.method = MHD_HTTP_METHOD_GET,
@ -1617,33 +1677,8 @@ handle_mhd_request (void *cls,
if (0 == strcasecmp (method,
MHD_HTTP_METHOD_POST))
{
const char *cl;
/* Maybe check for maximum upload size
and refuse requests if they are just too big. */
cl = MHD_lookup_connection_value (connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_CONTENT_LENGTH);
if (NULL != cl)
{
unsigned long long cv;
char dummy;
if (1 != sscanf (cl,
"%llu%c",
&cv,
&dummy))
{
/* Not valid HTTP request, just close connection. */
GNUNET_break_op (0);
return MHD_NO;
}
if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
{
GNUNET_break_op (0);
return TALER_MHD_reply_request_too_large (connection);
}
}
TALER_MHD_check_content_length (connection,
TALER_MHD_REQUEST_BUFFER_MAX);
}
}
@ -1870,6 +1905,17 @@ exchange_serve_process_config (void)
"valid relative time expected");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
"exchange",
"KYC_AML_TRIGGER",
&TEH_kyc_aml_trigger))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"KYC_AML_TRIGGER");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_config_get_currency (TEH_cfg,
&TEH_currency))
@ -1881,19 +1927,30 @@ exchange_serve_process_config (void)
}
if (GNUNET_OK !=
TALER_config_get_amount (TEH_cfg,
"taler",
"exchange",
"AML_THRESHOLD",
&TEH_aml_threshold))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Need amount in section `TALER' under `AML_THRESHOLD'\n");
"Need amount in section `exchange' under `AML_THRESHOLD'\n");
return GNUNET_SYSERR;
}
if (0 != strcmp (TEH_currency,
TEH_aml_threshold.currency))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Amount in section `TALER' under `AML_THRESHOLD' uses the wrong currency!\n");
"Amount in section `exchange' under `AML_THRESHOLD' uses the wrong currency!\n");
return GNUNET_SYSERR;
}
TEH_enable_rewards
= GNUNET_CONFIGURATION_get_value_yesno (
TEH_cfg,
"exchange",
"ENABLE_REWARDS");
if (GNUNET_SYSERR == TEH_enable_rewards)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Need YES or NO in section `exchange' under `ENABLE_REWARDS'\n");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@ -2215,6 +2272,7 @@ do_shutdown (void *cls)
mhd = TALER_MHD_daemon_stop ();
TEH_resume_keys_requests (true);
TEH_deposits_get_cleanup ();
TEH_reserves_get_cleanup ();
TEH_purses_get_cleanup ();
TEH_kyc_check_cleanup ();

View File

@ -64,6 +64,11 @@ extern int TEH_check_invariants_flag;
*/
extern int TEH_allow_keys_timetravel;
/**
* Option set to #GNUNET_YES if rewards are allowed.
*/
extern int TEH_enable_rewards;
/**
* Main directory with revocation data.
*/
@ -97,6 +102,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/
extern char *TEH_currency;
/**
* Name of the KYC-AML-trigger evaluation binary.
*/
extern char *TEH_kyc_aml_trigger;
/**
* What is the largest amount we allow a peer to
* merge into a reserve before always triggering

Some files were not shown because too many files have changed in this diff Show More