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 (18205B)


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