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


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