exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_exchange_p2p.c (18421B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014--2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU General Public License as
      7   published by the Free Software Foundation; either version 3, or
      8   (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file testing/test_exchange_p2p.c
     21  * @brief testcase to test exchange's P2P payments
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/platform.h"
     25 #include "taler/taler_attributes.h"
     26 #include "taler/taler_util.h"
     27 #include "taler/taler_signatures.h"
     28 #include "taler/taler_exchange_service.h"
     29 #include "taler/taler_json_lib.h"
     30 #include <gnunet/gnunet_util_lib.h>
     31 #include <gnunet/gnunet_testing_lib.h>
     32 #include <microhttpd.h>
     33 #include "taler/taler_bank_service.h"
     34 #include "taler/taler_fakebank_lib.h"
     35 #include "taler/taler_testing_lib.h"
     36 #include "taler/taler_extensions.h"
     37 
     38 /**
     39  * Configuration file we use.  One (big) configuration is used
     40  * for the various components for this test.
     41  */
     42 static char *config_file;
     43 
     44 /**
     45  * Our credentials.
     46  */
     47 struct TALER_TESTING_Credentials cred;
     48 
     49 /**
     50  * Some tests behave differently when using CS as we cannot
     51  * reuse the coin private key for different denominations
     52  * due to the derivation of it with the /csr values. Hence
     53  * some tests behave differently in CS mode, hence this
     54  * flag.
     55  */
     56 static bool uses_cs;
     57 
     58 /**
     59  * Execute the taler-exchange-wirewatch command with
     60  * our configuration file.
     61  *
     62  * @param label label to use for the command.
     63  */
     64 #define CMD_EXEC_WIREWATCH(label) \
     65         TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, \
     66                                            "exchange-account-2")
     67 
     68 /**
     69  * Execute the taler-exchange-aggregator, closer and transfer commands with
     70  * our configuration file.
     71  *
     72  * @param label label to use for the command.
     73  */
     74 #define CMD_EXEC_AGGREGATOR(label) \
     75         TALER_TESTING_cmd_sleep ("sleep-before-aggregator", 2), \
     76         TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \
     77         TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file)
     78 
     79 
     80 /**
     81  * Run wire transfer of funds from some user's account to the
     82  * exchange.
     83  *
     84  * @param label label to use for the command.
     85  * @param amount amount to transfer, i.e. "EUR:1"
     86  */
     87 #define CMD_TRANSFER_TO_EXCHANGE(label,amount)                  \
     88         TALER_TESTING_cmd_admin_add_incoming (label, amount,    \
     89                                               &cred.ba,         \
     90                                               cred.user42_payto)
     91 
     92 /**
     93  * Main function that will tell the interpreter what commands to
     94  * run.
     95  *
     96  * @param cls closure
     97  * @param is interpreter we use to run commands
     98  */
     99 static void
    100 run (void *cls,
    101      struct TALER_TESTING_Interpreter *is)
    102 {
    103   /**
    104    * Test withdrawal plus spending.
    105    */
    106   struct TALER_TESTING_Command withdraw[] = {
    107     /**
    108      * Move money to the exchange's bank account.
    109      */
    110     CMD_TRANSFER_TO_EXCHANGE (
    111       "create-reserve-1",
    112       "EUR:5.04"),
    113     CMD_TRANSFER_TO_EXCHANGE (
    114       "create-reserve-2",
    115       "EUR:5.01"),
    116     TALER_TESTING_cmd_reserve_poll (
    117       "poll-reserve-1",
    118       "create-reserve-1",
    119       "EUR:5.04",
    120       GNUNET_TIME_UNIT_MINUTES,
    121       MHD_HTTP_OK),
    122     TALER_TESTING_cmd_check_bank_admin_transfer (
    123       "check-create-reserve-1",
    124       "EUR:5.04",
    125       cred.user42_payto,
    126       cred.exchange_payto,
    127       "create-reserve-1"),
    128     TALER_TESTING_cmd_check_bank_admin_transfer (
    129       "check-create-reserve-2",
    130       "EUR:5.01",
    131       cred.user42_payto,
    132       cred.exchange_payto,
    133       "create-reserve-2"),
    134     /**
    135      * Make a reserve exist, according to the previous
    136      * transfer.
    137      */
    138     CMD_EXEC_WIREWATCH ("wirewatch-1"),
    139     TALER_TESTING_cmd_reserve_poll_finish (
    140       "finish-poll-reserve-1",
    141       GNUNET_TIME_UNIT_SECONDS,
    142       "poll-reserve-1"),
    143     /**
    144      * Withdraw EUR:5.
    145      */
    146     TALER_TESTING_cmd_withdraw_amount (
    147       "withdraw-coin-1",
    148       "create-reserve-1",
    149       "EUR:5",
    150       0,    /* age restriction off */
    151       MHD_HTTP_OK),
    152     /**
    153      * Check the reserve is depleted.
    154      */
    155     TALER_TESTING_cmd_status (
    156       "status-1",
    157       "create-reserve-1",
    158       "EUR:0.03",
    159       MHD_HTTP_OK),
    160     TALER_TESTING_cmd_end ()
    161   };
    162   struct TALER_TESTING_Command push[] = {
    163     TALER_TESTING_cmd_purse_create_with_deposit (
    164       "purse-with-deposit-for-delete",
    165       MHD_HTTP_OK,
    166       "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
    167       true, /* upload contract */
    168       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    169       "withdraw-coin-1",
    170       "EUR:1.01",
    171       NULL),
    172     TALER_TESTING_cmd_purse_delete (
    173       "purse-with-deposit-delete",
    174       MHD_HTTP_NO_CONTENT,
    175       "purse-with-deposit-for-delete"),
    176     TALER_TESTING_cmd_purse_create_with_deposit (
    177       "purse-with-deposit",
    178       MHD_HTTP_OK,
    179       "{\"amount\":\"EUR:0.99\",\"summary\":\"ice cream\"}",
    180       true, /* upload contract */
    181       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    182       "withdraw-coin-1",
    183       "EUR:1.00",
    184       NULL),
    185     TALER_TESTING_cmd_purse_poll (
    186       "push-poll-purse-before-merge",
    187       MHD_HTTP_OK,
    188       "purse-with-deposit",
    189       "EUR:0.99",
    190       true,
    191       GNUNET_TIME_UNIT_MINUTES),
    192     TALER_TESTING_cmd_contract_get (
    193       "push-get-contract",
    194       MHD_HTTP_OK,
    195       true, /* for merge */
    196       "purse-with-deposit"),
    197     TALER_TESTING_cmd_purse_merge (
    198       "purse-merge-into-reserve",
    199       MHD_HTTP_OK,
    200       "push-get-contract",
    201       "create-reserve-1"),
    202     TALER_TESTING_cmd_purse_poll_finish (
    203       "push-merge-purse-poll-finish",
    204       GNUNET_TIME_relative_multiply (
    205         GNUNET_TIME_UNIT_SECONDS,
    206         5),
    207       "push-poll-purse-before-merge"),
    208     TALER_TESTING_cmd_status (
    209       "push-check-post-merge-reserve-balance-get",
    210       "create-reserve-1",
    211       "EUR:1.02",
    212       MHD_HTTP_OK),
    213     /* POST history doesn't yet support P2P transfers */
    214     TALER_TESTING_cmd_reserve_history (
    215       "push-check-post-merge-reserve-balance-post",
    216       "create-reserve-1",
    217       "EUR:1.02",
    218       MHD_HTTP_OK),
    219     /* Test conflicting merge */
    220     TALER_TESTING_cmd_purse_merge (
    221       "purse-merge-into-reserve",
    222       MHD_HTTP_CONFLICT,
    223       "push-get-contract",
    224       "create-reserve-2"),
    225 
    226     TALER_TESTING_cmd_end ()
    227   };
    228   struct TALER_TESTING_Command pull[] = {
    229     TALER_TESTING_cmd_purse_create_with_reserve (
    230       "purse-create-with-reserve",
    231       MHD_HTTP_OK,
    232       "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
    233       true /* upload contract */,
    234       true /* pay purse fee */,
    235       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    236       "create-reserve-1"),
    237     TALER_TESTING_cmd_contract_get (
    238       "pull-get-contract",
    239       MHD_HTTP_OK,
    240       false, /* for deposit */
    241       "purse-create-with-reserve"),
    242     TALER_TESTING_cmd_purse_poll (
    243       "pull-poll-purse-before-deposit",
    244       MHD_HTTP_OK,
    245       "purse-create-with-reserve",
    246       "EUR:1",
    247       false,
    248       GNUNET_TIME_UNIT_MINUTES),
    249     TALER_TESTING_cmd_purse_deposit_coins (
    250       "purse-deposit-coins",
    251       MHD_HTTP_OK,
    252       0 /* min age */,
    253       "purse-create-with-reserve",
    254       "withdraw-coin-1",
    255       "EUR:1.01",
    256       NULL),
    257     TALER_TESTING_cmd_purse_poll_finish (
    258       "pull-deposit-purse-poll-finish",
    259       GNUNET_TIME_relative_multiply (
    260         GNUNET_TIME_UNIT_SECONDS,
    261         5),
    262       "pull-poll-purse-before-deposit"),
    263     TALER_TESTING_cmd_status (
    264       "pull-check-post-merge-reserve-balance-get",
    265       "create-reserve-1",
    266       "EUR:2.02",
    267       MHD_HTTP_OK),
    268     TALER_TESTING_cmd_reserve_history (
    269       "push-check-post-merge-reserve-balance-post",
    270       "create-reserve-1",
    271       "EUR:2.02",
    272       MHD_HTTP_OK),
    273     TALER_TESTING_cmd_purse_deposit_coins (
    274       "purse-deposit-coins-idempotent",
    275       MHD_HTTP_OK,
    276       0 /* min age */,
    277       "purse-create-with-reserve",
    278       "withdraw-coin-1",
    279       "EUR:1.01",
    280       NULL),
    281     /* create 2nd purse for a deposit conflict */
    282     TALER_TESTING_cmd_purse_create_with_reserve (
    283       "purse-create-with-reserve-2",
    284       MHD_HTTP_OK,
    285       "{\"amount\":\"EUR:4\",\"summary\":\"beer\"}",
    286       true /* upload contract */,
    287       true /* pay purse fee */,
    288       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    289       "create-reserve-1"),
    290     TALER_TESTING_cmd_purse_deposit_coins (
    291       "purse-deposit-coins-conflict",
    292       MHD_HTTP_CONFLICT,
    293       0 /* min age */,
    294       "purse-create-with-reserve-2",
    295       "withdraw-coin-1",
    296       "EUR:4.01",
    297       NULL),
    298     TALER_TESTING_cmd_end ()
    299   };
    300 
    301   struct TALER_TESTING_Command expire[] = {
    302     TALER_TESTING_cmd_purse_create_with_reserve (
    303       "purse-create-with-reserve-expire",
    304       MHD_HTTP_OK,
    305       "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}",
    306       true /* upload contract */,
    307       true /* pay purse fee */,
    308       GNUNET_TIME_relative_multiply (
    309         GNUNET_TIME_UNIT_SECONDS,
    310         1), /* expiration */
    311       "create-reserve-1"),
    312     TALER_TESTING_cmd_purse_poll (
    313       "pull-poll-purse-before-expire",
    314       MHD_HTTP_GONE,
    315       "purse-create-with-reserve-expire",
    316       "EUR:1",
    317       false,
    318       GNUNET_TIME_UNIT_MINUTES),
    319     TALER_TESTING_cmd_purse_create_with_deposit (
    320       "purse-with-deposit-expire",
    321       MHD_HTTP_OK,
    322       "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
    323       true, /* upload contract */
    324       GNUNET_TIME_relative_multiply (
    325         GNUNET_TIME_UNIT_SECONDS,
    326         1), /* expiration */
    327       "withdraw-coin-1",
    328       "EUR:1.02",
    329       NULL),
    330     TALER_TESTING_cmd_purse_poll (
    331       "push-poll-purse-before-expire",
    332       MHD_HTTP_GONE,
    333       "purse-with-deposit-expire",
    334       "EUR:1",
    335       true, /* wait for merge */
    336       GNUNET_TIME_UNIT_MINUTES),
    337     /* This should fail, as too much of the coin
    338        is already spend / in a purse */
    339     TALER_TESTING_cmd_purse_create_with_deposit (
    340       "purse-with-deposit-overspending",
    341       MHD_HTTP_CONFLICT,
    342       "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}",
    343       true, /* upload contract */
    344       GNUNET_TIME_relative_multiply (
    345         GNUNET_TIME_UNIT_SECONDS,
    346         1), /* expiration */
    347       "withdraw-coin-1",
    348       "EUR:2.01",
    349       NULL),
    350     TALER_TESTING_cmd_sleep (
    351       "sleep",
    352       2 /* seconds */),
    353     TALER_TESTING_cmd_exec_expire (
    354       "exec-expire",
    355       config_file),
    356     TALER_TESTING_cmd_purse_poll_finish (
    357       "push-merge-purse-poll-finish-expire",
    358       GNUNET_TIME_relative_multiply (
    359         GNUNET_TIME_UNIT_SECONDS,
    360         15),
    361       "push-poll-purse-before-expire"),
    362     TALER_TESTING_cmd_purse_poll_finish (
    363       "pull-deposit-purse-poll-expire-finish",
    364       GNUNET_TIME_relative_multiply (
    365         GNUNET_TIME_UNIT_SECONDS,
    366         15),
    367       "pull-poll-purse-before-expire"),
    368     /* coin was refunded, so now this should be OK */
    369     /* This should fail, as too much of the coin
    370        is already spend / in a purse */
    371     TALER_TESTING_cmd_purse_create_with_deposit (
    372       "purse-with-deposit-refunded",
    373       MHD_HTTP_OK,
    374       "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}",
    375       true, /* upload contract */
    376       GNUNET_TIME_relative_multiply (
    377         GNUNET_TIME_UNIT_SECONDS,
    378         1), /* expiration */
    379       "withdraw-coin-1",
    380       "EUR:2.01",
    381       NULL),
    382     TALER_TESTING_cmd_end ()
    383   };
    384   struct TALER_TESTING_Command reserves[] = {
    385     CMD_TRANSFER_TO_EXCHANGE (
    386       "create-reserve-100",
    387       "EUR:1.04"),
    388     TALER_TESTING_cmd_check_bank_admin_transfer (
    389       "check-create-reserve-100",
    390       "EUR:1.04",
    391       cred.user42_payto,
    392       cred.exchange_payto,
    393       "create-reserve-100"),
    394     CMD_TRANSFER_TO_EXCHANGE (
    395       "create-reserve-101",
    396       "EUR:1.04"),
    397     TALER_TESTING_cmd_check_bank_admin_transfer (
    398       "check-create-reserve-101",
    399       "EUR:1.04",
    400       cred.user42_payto,
    401       cred.exchange_payto,
    402       "create-reserve-101"),
    403     CMD_EXEC_WIREWATCH ("wirewatch-100"),
    404     TALER_TESTING_cmd_withdraw_amount (
    405       "withdraw-coin-100",
    406       "create-reserve-100",
    407       "EUR:1",
    408       0,       /* age restriction off */
    409       MHD_HTTP_OK),
    410     TALER_TESTING_cmd_reserve_open (
    411       "reserve-open-101-fail",
    412       "create-reserve-101",
    413       "EUR:0",
    414       GNUNET_TIME_UNIT_YEARS,
    415       5,     /* min purses */
    416       MHD_HTTP_PAYMENT_REQUIRED,
    417       NULL,
    418       NULL),
    419     TALER_TESTING_cmd_reserve_open (
    420       "reserve-open-101-ok-a",
    421       "create-reserve-101",
    422       "EUR:0.01",
    423       GNUNET_TIME_UNIT_MONTHS,
    424       1,                               /* min purses */
    425       MHD_HTTP_OK,
    426       NULL,
    427       NULL),
    428     TALER_TESTING_cmd_status (
    429       "status-101-open-paid",
    430       "create-reserve-101",
    431       "EUR:1.03",
    432       MHD_HTTP_OK),
    433     TALER_TESTING_cmd_reserve_open (
    434       "reserve-open-101-ok-b",
    435       "create-reserve-101",
    436       "EUR:0",
    437       GNUNET_TIME_UNIT_MONTHS,
    438       2,            /* min purses */
    439       MHD_HTTP_OK,
    440       "withdraw-coin-100",
    441       "EUR:0.03",  /* 0.02 for the reserve open, 0.01 for deposit fee */
    442       NULL,
    443       NULL),
    444     /* Use purse creation with purse quota here */
    445     TALER_TESTING_cmd_purse_create_with_reserve (
    446       "purse-create-with-reserve-101-a",
    447       MHD_HTTP_OK,
    448       "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
    449       true /* upload contract */,
    450       false /* pay purse fee */,
    451       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    452       "create-reserve-101"),
    453     TALER_TESTING_cmd_purse_create_with_reserve (
    454       "purse-create-with-reserve-101-b",
    455       MHD_HTTP_OK,
    456       "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
    457       true /* upload contract */,
    458       false /* pay purse fee */,
    459       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    460       "create-reserve-101"),
    461     TALER_TESTING_cmd_purse_create_with_reserve (
    462       "purse-create-with-reserve-101-fail",
    463       MHD_HTTP_CONFLICT,
    464       "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
    465       true /* upload contract */,
    466       false /* pay purse fee */,
    467       GNUNET_TIME_UNIT_MINUTES, /* expiration */
    468       "create-reserve-101"),
    469     TALER_TESTING_cmd_reserve_get_attestable (
    470       "reserve-101-attestable",
    471       "create-reserve-101",
    472       MHD_HTTP_NOT_FOUND,
    473       NULL),
    474     TALER_TESTING_cmd_reserve_get_attestable (
    475       "reserve-101-attest",
    476       "create-reserve-101",
    477       MHD_HTTP_NOT_FOUND,
    478       "nx-attribute-name",
    479       NULL),
    480     TALER_TESTING_cmd_oauth_with_birthdate (
    481       "start-oauth-service",
    482       "2015-00-00",
    483       6666),
    484     TALER_TESTING_cmd_reserve_close (
    485       "reserve-101-close-kyc",
    486       "create-reserve-101",
    487       /* 44 => not to origin */
    488       cred.user44_payto,
    489       MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS),
    490     TALER_TESTING_cmd_admin_add_kycauth (
    491       "setup-account-key",
    492       "EUR:0.01",
    493       &cred.ba,
    494       cred.user44_payto,
    495       NULL /* create new key */),
    496     CMD_EXEC_WIREWATCH (
    497       "import-kyc-account"),
    498     TALER_TESTING_cmd_check_kyc_get (
    499       "check-kyc-close-pending",
    500       "reserve-101-close-kyc",
    501       "setup-account-key",
    502       TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER,
    503       MHD_HTTP_ACCEPTED),
    504     TALER_TESTING_cmd_get_kyc_info (
    505       "get-kyc-info",
    506       "check-kyc-close-pending",
    507       MHD_HTTP_OK),
    508     TALER_TESTING_cmd_post_kyc_start (
    509       "start-kyc-process",
    510       "get-kyc-info",
    511       0,
    512       MHD_HTTP_OK),
    513     TALER_TESTING_cmd_proof_kyc_oauth2 (
    514       "proof-close-kyc",
    515       "reserve-101-close-kyc",
    516       "test-oauth2",
    517       "pass",
    518       MHD_HTTP_SEE_OTHER),
    519     TALER_TESTING_cmd_check_kyc_get (
    520       "check-kyc-close-ok",
    521       "reserve-101-close-kyc",
    522       "setup-account-key",
    523       TALER_EXCHANGE_KLPT_KYC_OK,
    524       MHD_HTTP_OK),
    525     /* Now it should pass */
    526     TALER_TESTING_cmd_reserve_close (
    527       "reserve-101-close",
    528       "create-reserve-101",
    529       /* 44 => not to origin */
    530       cred.user44_payto,
    531       MHD_HTTP_OK),
    532     TALER_TESTING_cmd_exec_closer (
    533       "close-reserves-101",
    534       config_file,
    535       "EUR:1.02",
    536       "EUR:0.01",
    537       "create-reserve-101"),
    538     TALER_TESTING_cmd_exec_transfer (
    539       "close-reserves-101-transfer",
    540       config_file),
    541     TALER_TESTING_cmd_status (
    542       "reserve-101-closed-status",
    543       "create-reserve-101",
    544       "EUR:0",
    545       MHD_HTTP_OK),
    546     TALER_TESTING_cmd_end ()
    547   };
    548 
    549   struct TALER_TESTING_Command commands[] = {
    550     TALER_TESTING_cmd_run_fakebank ("run-fakebank",
    551                                     cred.cfg,
    552                                     "exchange-account-2"),
    553     TALER_TESTING_cmd_system_start ("start-taler",
    554                                     config_file,
    555                                     "-e",
    556                                     NULL),
    557     TALER_TESTING_cmd_get_exchange ("get-exchange",
    558                                     cred.cfg,
    559                                     NULL,
    560                                     true,
    561                                     true),
    562     TALER_TESTING_cmd_batch ("withdraw",
    563                              withdraw),
    564     TALER_TESTING_cmd_batch ("push",
    565                              push),
    566     TALER_TESTING_cmd_batch ("pull",
    567                              pull),
    568     TALER_TESTING_cmd_batch ("expire",
    569                              expire),
    570     TALER_TESTING_cmd_batch ("reserves",
    571                              reserves),
    572     /* End the suite. */
    573     TALER_TESTING_cmd_end ()
    574   };
    575 
    576   (void) cls;
    577   TALER_TESTING_run (is,
    578                      commands);
    579 }
    580 
    581 
    582 int
    583 main (int argc,
    584       char *const *argv)
    585 {
    586   (void) argc;
    587   {
    588     char *cipher;
    589 
    590     cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
    591     GNUNET_assert (NULL != cipher);
    592     uses_cs = (0 == strcmp (cipher, "cs"));
    593     GNUNET_asprintf (&config_file,
    594                      "test_exchange_api-%s.conf",
    595                      cipher);
    596     GNUNET_free (cipher);
    597   }
    598   return TALER_TESTING_main (argv,
    599                              "INFO",
    600                              config_file,
    601                              "exchange-account-2",
    602                              TALER_TESTING_BS_FAKEBANK,
    603                              &cred,
    604                              &run,
    605                              NULL);
    606 }
    607 
    608 
    609 /* end of test_exchange_p2p.c */