sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

test_sync_api.c (24926B)


      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_sync_api.c
     21  * @brief testcase to test sync's HTTP API interface
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 #include <taler/taler_util.h>
     26 #include <taler/taler_signatures.h>
     27 #include <taler/taler_exchange_service.h>
     28 #include <taler/taler_json_lib.h>
     29 #include <gnunet/gnunet_util_lib.h>
     30 #include <microhttpd.h>
     31 #include <taler/taler_bank_service.h>
     32 #include <taler/taler_fakebank_lib.h>
     33 #include <taler/taler_testing_lib.h>
     34 #include <taler/taler_merchant_testing_lib.h>
     35 #include <taler/taler_error_codes.h>
     36 #include "sync/sync_service.h"
     37 #include "sync/sync_testing_lib.h"
     38 #include "sync_util.h"
     39 
     40 
     41 /**
     42  * Configuration file we use.  One (big) configuration is used
     43  * for the various components for this test.
     44  */
     45 #define CONFIG_FILE "test_sync_api.conf"
     46 
     47 /**
     48  * Account number of some user.
     49  */
     50 #define USER_ACCOUNT_NAME "62"
     51 
     52 /**
     53  * Account number used by the merchant
     54  */
     55 #define MERCHANT_ACCOUNT_NAME "3"
     56 
     57 /**
     58  * Payto URI of the customer (payer).
     59  */
     60 static struct TALER_FullPayto payer_payto;
     61 
     62 /**
     63  * Payto URI of the merchant (receiver).
     64  */
     65 static struct TALER_FullPayto merchant_payto;
     66 
     67 /**
     68  * Configuration of the bank.
     69  */
     70 static struct TALER_TESTING_Credentials cred;
     71 
     72 /**
     73  * Merchant base URL.
     74  */
     75 static const char *merchant_url = "http://localhost:8080/";
     76 
     77 /**
     78  * Sync base URL.
     79  */
     80 static const char *sync_url = "http://localhost:8084/";
     81 
     82 
     83 /**
     84  * Execute the taler-exchange-wirewatch command with
     85  * our configuration file.
     86  *
     87  * @param label label to use for the command.
     88  */
     89 static struct TALER_TESTING_Command
     90 cmd_exec_wirewatch (const char *label)
     91 {
     92   return TALER_TESTING_cmd_exec_wirewatch (label,
     93                                            CONFIG_FILE);
     94 }
     95 
     96 
     97 /**
     98  * Run wire transfer of funds from some user's account to the
     99  * exchange.
    100  *
    101  * @param label label to use for the command.
    102  * @param amount amount to transfer, i.e. "EUR:1"
    103  * @param url exchange_url
    104  */
    105 static struct TALER_TESTING_Command
    106 cmd_transfer_to_exchange (const char *label,
    107                           const char *amount)
    108 {
    109   return TALER_TESTING_cmd_admin_add_incoming (label,
    110                                                amount,
    111                                                &cred.ba,
    112                                                payer_payto);
    113 }
    114 
    115 
    116 /**
    117  * Main function that will tell the interpreter what commands to
    118  * run.
    119  *
    120  * @param cls closure
    121  * @param is interpreter state
    122  */
    123 static void
    124 run (void *cls,
    125      struct TALER_TESTING_Interpreter *is)
    126 {
    127   /* Payload of the blocks we upload.  data[0] is the initial content of
    128      block A, which is later updated to data[1]; hashes[] holds the
    129      hashes of the *final* content of blocks A, C and D. */
    130   static const char *const data[] = {
    131     "Hello World",
    132     "Block B data",
    133     "Block C data",
    134     "Block D data"
    135   };
    136   static const char obj_data_A[] = "object-A-data";
    137   static const char obj_data_B[] = "object-B-data";
    138   /* Nonces of blocks A, C and D, in chain order. */
    139   struct SYNC_BlockNonce nonces[3];
    140   /* Hashes of blocks A, C and D, in chain order. */
    141   struct GNUNET_HashCode hashes[3];
    142   /* Nonce of a block that is never successfully uploaded. */
    143   struct SYNC_BlockNonce nonce_stale;
    144   /* Nonce that never exists on the server. */
    145   struct SYNC_BlockNonce bogus_nonce;
    146   /* Nonces of the two blocks used for the refcount tests. */
    147   struct SYNC_BlockNonce ref_nonce;
    148   struct SYNC_BlockNonce ref_nonce2;
    149   /* Nonce of a block whose append is rejected for a bad object ref. */
    150   struct SYNC_BlockNonce orphan_nonce;
    151   /* Nonce of the block appended after that rejected append. */
    152   struct SYNC_BlockNonce tail_nonce;
    153   /* UIDs of the two objects used for the refcount tests. */
    154   struct SYNC_ObjectUID obj_uids[2];
    155   /* UID of an object that is never uploaded. */
    156   struct SYNC_ObjectUID unknown_uid;
    157   const int16_t ref_incs[2] = { 1, 1 };
    158   const int16_t ref_decs[2] = { -1, -1 };
    159   const int16_t ref_inc1[1] = { 1 };
    160   const int16_t ref_dec2[1] = { -2 };
    161 
    162   (void) cls;
    163   for (unsigned int i = 0; i < 3; i++)
    164     SYNC_block_nonce_generate (&nonces[i]);
    165   SYNC_block_nonce_generate (&nonce_stale);
    166   SYNC_block_nonce_generate (&bogus_nonce);
    167   SYNC_block_nonce_generate (&ref_nonce);
    168   SYNC_block_nonce_generate (&ref_nonce2);
    169   SYNC_block_nonce_generate (&orphan_nonce);
    170   SYNC_block_nonce_generate (&tail_nonce);
    171   for (unsigned int i = 0; i < 2; i++)
    172     GNUNET_CRYPTO_random_block (&obj_uids[i],
    173                                 sizeof (obj_uids[i]));
    174   GNUNET_CRYPTO_random_block (&unknown_uid,
    175                               sizeof (unknown_uid));
    176   for (unsigned int i = 0; i < 3; i++)
    177     GNUNET_CRYPTO_hash (data[i + 1],
    178                         strlen (data[i + 1]),
    179                         &hashes[i]);
    180 
    181   {
    182     struct TALER_TESTING_Command commands[] = {
    183       TALER_TESTING_cmd_system_start ("start-taler",
    184                                       CONFIG_FILE,
    185                                       "-efms",
    186                                       "-u", "exchange-account-exchange",
    187                                       "-r", "merchant-exchange-default",
    188                                       NULL),
    189       TALER_TESTING_cmd_get_exchange ("get-exchange",
    190                                       cred.cfg,
    191                                       NULL,
    192                                       true,
    193                                       true),
    194       TALER_TESTING_cmd_merchant_post_instances ("instance-create-admin",
    195                                                  merchant_url,
    196                                                  "admin",
    197                                                  MHD_HTTP_NO_CONTENT),
    198       TALER_TESTING_cmd_merchant_post_account (
    199         "instance-create-default-account",
    200         merchant_url,
    201         merchant_payto,
    202         NULL, NULL,
    203         MHD_HTTP_OK),
    204 
    205       /**
    206        * Move money to the exchange's bank account.
    207        */
    208       cmd_transfer_to_exchange ("create-reserve-1",
    209                                 "EUR:10.02"),
    210       /**
    211        * Make a reserve exist, according to the previous
    212        * transfer.
    213        */
    214       cmd_exec_wirewatch ("wirewatch-1"),
    215       TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
    216                                          "create-reserve-1",
    217                                          "EUR:5",
    218                                          0,
    219                                          MHD_HTTP_OK),
    220       TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
    221                                          "create-reserve-1",
    222                                          "EUR:5",
    223                                          0,
    224                                          MHD_HTTP_OK),
    225 
    226       /* Upload first block; no lifetime yet → 402 Payment Required */
    227       SYNC_TESTING_cmd_block_append ("upload-block-1",
    228                                      sync_url,
    229                                      NULL,
    230                                      &nonces[0],
    231                                      MHD_HTTP_PAYMENT_REQUIRED,
    232                                      data[0],
    233                                      strlen (data[0]),
    234                                      true,
    235                                      0,
    236                                      NULL,
    237                                      NULL),
    238       /* Claim the order from the merchant */
    239       TALER_TESTING_cmd_merchant_claim_order ("fetch-proposal",
    240                                               merchant_url,
    241                                               MHD_HTTP_OK,
    242                                               "upload-block-1",
    243                                               NULL),
    244       /* Pay with one withdrawn coin */
    245       TALER_TESTING_cmd_merchant_pay_order ("pay-account",
    246                                             merchant_url,
    247                                             MHD_HTTP_OK,
    248                                             "fetch-proposal",
    249                                             "withdraw-coin-1",
    250                                             "EUR:5",
    251                                             "EUR:4.99",
    252                                             "session-id"),
    253       /* Retry same upload; server finds paid order → 204 */
    254       SYNC_TESTING_cmd_block_append ("upload-block-2",
    255                                      sync_url,
    256                                      "upload-block-1",
    257                                      &nonces[0],
    258                                      MHD_HTTP_NO_CONTENT,
    259                                      data[0],
    260                                      strlen (data[0]),
    261                                      true,
    262                                      0,
    263                                      NULL,
    264                                      NULL),
    265       /* Update the existing block with different content */
    266       SYNC_TESTING_cmd_block_update ("upload-block-B",
    267                                      sync_url,
    268                                      "upload-block-2",
    269                                      &nonces[0],
    270                                      NULL,
    271                                      MHD_HTTP_NO_CONTENT,
    272                                      data[1],
    273                                      strlen (data[1]),
    274                                      0,
    275                                      NULL,
    276                                      NULL),
    277       /* Append a new block C at the end of block B */
    278       SYNC_TESTING_cmd_block_append ("upload-block-C",
    279                                      sync_url,
    280                                      "upload-block-B",
    281                                      &nonces[1],
    282                                      MHD_HTTP_NO_CONTENT,
    283                                      data[2],
    284                                      strlen (data[2]),
    285                                      false,
    286                                      0,
    287                                      NULL,
    288                                      NULL),
    289       /* List blocks for the account */
    290       SYNC_TESTING_cmd_block_list ("list-blocks",
    291                                    sync_url,
    292                                    "upload-block-2",
    293                                    10,
    294                                    NULL,
    295                                    MHD_HTTP_OK,
    296                                    2,
    297                                    nonces,
    298                                    hashes),
    299 
    300       /* Append with stale prev_nonce (nonces[0]),
    301          but last_block is now nonces[1] → 409 */
    302       SYNC_TESTING_cmd_block_append ("append-stale-prev",
    303                                      sync_url,
    304                                      "upload-block-B",
    305                                      &nonce_stale,
    306                                      MHD_HTTP_CONFLICT,
    307                                      data[0],
    308                                      strlen (data[0]),
    309                                      false,
    310                                      0,
    311                                      NULL,
    312                                      NULL),
    313       /* Update with stale old_data_hash
    314          (references upload-block-2 whose curr_hash is hash("Hello World"),
    315           but block now has hash("Block B data")) → 409 */
    316       SYNC_TESTING_cmd_block_update ("update-conflict",
    317                                      sync_url,
    318                                      "upload-block-2",
    319                                      &nonces[0],
    320                                      NULL,
    321                                      MHD_HTTP_CONFLICT,
    322                                      data[2],
    323                                      strlen (data[2]),
    324                                      0,
    325                                      NULL,
    326                                      NULL),
    327       /* Re-upload same data → 304 Not Modified */
    328       SYNC_TESTING_cmd_block_append ("reupload-same-data",
    329                                      sync_url,
    330                                      "upload-block-B",
    331                                      &nonces[0],
    332                                      MHD_HTTP_NOT_MODIFIED,
    333                                      data[1],
    334                                      strlen (data[1]),
    335                                      true,
    336                                      0,
    337                                      NULL,
    338                                      NULL),
    339       /* List with non-existent start_nonce → 404 */
    340       SYNC_TESTING_cmd_block_list ("list-bogus-start",
    341                                    sync_url,
    342                                    "upload-block-2",
    343                                    10,
    344                                    &bogus_nonce,
    345                                    MHD_HTTP_NOT_FOUND,
    346                                    0,
    347                                    NULL,
    348                                    NULL),
    349       /* Final list confirms error cases left state intact */
    350       SYNC_TESTING_cmd_block_list ("list-blocks-final",
    351                                    sync_url,
    352                                    "upload-block-2",
    353                                    10,
    354                                    NULL,
    355                                    MHD_HTTP_OK,
    356                                    2,
    357                                    nonces,
    358                                    hashes),
    359 
    360       /* ========== Delete block tests ========== */
    361 
    362       /* Upload a third block D */
    363       SYNC_TESTING_cmd_block_append ("upload-block-D",
    364                                      sync_url,
    365                                      "upload-block-C",
    366                                      &nonces[2],
    367                                      MHD_HTTP_NO_CONTENT,
    368                                      data[3],
    369                                      strlen (data[3]),
    370                                      false,
    371                                      0,
    372                                      NULL,
    373                                      NULL),
    374       /* List all 3 blocks */
    375       SYNC_TESTING_cmd_block_list ("list-3-blocks",
    376                                    sync_url,
    377                                    "upload-block-D",
    378                                    10,
    379                                    NULL,
    380                                    MHD_HTTP_OK,
    381                                    3,
    382                                    nonces,
    383                                    hashes),
    384       /* Delete block D */
    385       SYNC_TESTING_cmd_block_delete ("delete-block-D",
    386                                      sync_url,
    387                                      "upload-block-D",
    388                                      MHD_HTTP_NO_CONTENT,
    389                                      0,
    390                                      NULL,
    391                                      NULL),
    392       /* List: only A and B remain */
    393       SYNC_TESTING_cmd_block_list ("list-after-delete-D",
    394                                    sync_url,
    395                                    "upload-block-2",
    396                                    10,
    397                                    NULL,
    398                                    MHD_HTTP_OK,
    399                                    2,
    400                                    nonces,
    401                                    hashes),
    402       /* Delete block D again → 404 (already gone) */
    403       SYNC_TESTING_cmd_block_delete ("delete-block-D-again",
    404                                      sync_url,
    405                                      "upload-block-D",
    406                                      MHD_HTTP_NOT_FOUND,
    407                                      0,
    408                                      NULL,
    409                                      NULL),
    410       /* Final list confirms all is well */
    411       SYNC_TESTING_cmd_block_list ("list-blocks-end",
    412                                    sync_url,
    413                                    "upload-block-2",
    414                                    10,
    415                                    NULL,
    416                                    MHD_HTTP_OK,
    417                                    2,
    418                                    nonces,
    419                                    hashes),
    420 
    421       /* ========== Object store and refcount tests ========== */
    422 
    423       /* Upload object A */
    424       SYNC_TESTING_cmd_object_upload ("upload-obj-A",
    425                                       sync_url,
    426                                       "upload-block-2",
    427                                       &obj_uids[0],
    428                                       MHD_HTTP_NO_CONTENT,
    429                                       obj_data_A,
    430                                       sizeof obj_data_A - 1),
    431       /* Upload object B */
    432       SYNC_TESTING_cmd_object_upload ("upload-obj-B",
    433                                       sync_url,
    434                                       "upload-block-2",
    435                                       &obj_uids[1],
    436                                       MHD_HTTP_NO_CONTENT,
    437                                       obj_data_B,
    438                                       sizeof obj_data_B - 1),
    439 
    440       /* Fetch object A and verify content and hash */
    441       SYNC_TESTING_cmd_object_fetch ("fetch-obj-A",
    442                                      sync_url,
    443                                      "upload-block-2",
    444                                      &obj_uids[0],
    445                                      MHD_HTTP_OK,
    446                                      obj_data_A,
    447                                      sizeof obj_data_A - 1),
    448 
    449       /* Fetch object B and verify content and hash */
    450       SYNC_TESTING_cmd_object_fetch ("fetch-obj-B",
    451                                      sync_url,
    452                                      "upload-block-2",
    453                                      &obj_uids[1],
    454                                      MHD_HTTP_OK,
    455                                      obj_data_B,
    456                                      sizeof obj_data_B - 1),
    457 
    458       /* Try uploading a duplicate UID → idempotent (204) */
    459       SYNC_TESTING_cmd_object_upload ("upload-obj-A-dup",
    460                                       sync_url,
    461                                       "upload-block-2",
    462                                       &obj_uids[0],
    463                                       MHD_HTTP_NO_CONTENT,
    464                                       obj_data_B,
    465                                       sizeof obj_data_B - 1),
    466 
    467       /* Upload a block that references objects A (+1) and B (+1) */
    468       SYNC_TESTING_cmd_block_append ("upload-block-refs",
    469                                      sync_url,
    470                                      "upload-block-C",
    471                                      &ref_nonce,
    472                                      MHD_HTTP_NO_CONTENT,
    473                                      data[0],
    474                                      strlen (data[0]),
    475                                      false,
    476                                      2,
    477                                      obj_uids,
    478                                      ref_incs),
    479 
    480       /* Delete the referencing block with matching refs (decrement).
    481          Objects reach refcount 0 and are auto-deleted from the DB. */
    482       SYNC_TESTING_cmd_block_delete ("delete-block-refs",
    483                                      sync_url,
    484                                      "upload-block-refs",
    485                                      MHD_HTTP_NO_CONTENT,
    486                                      2,
    487                                      obj_uids,
    488                                      ref_decs),
    489 
    490       /* Re-upload object A (it was deleted when refcount hit 0) */
    491       SYNC_TESTING_cmd_object_upload ("upload-obj-A-again",
    492                                       sync_url,
    493                                       "upload-block-2",
    494                                       &obj_uids[0],
    495                                       MHD_HTTP_NO_CONTENT,
    496                                       obj_data_A,
    497                                       sizeof obj_data_A - 1),
    498 
    499       /* Upload another block referencing A (+1) */
    500       SYNC_TESTING_cmd_block_append ("upload-block-refs2",
    501                                      sync_url,
    502                                      "upload-block-C",
    503                                      &ref_nonce2,
    504                                      MHD_HTTP_NO_CONTENT,
    505                                      data[0],
    506                                      strlen (data[0]),
    507                                      false,
    508                                      1,
    509                                      obj_uids,
    510                                      ref_inc1),
    511 
    512       /* Try to delete with refs that would underflow → 409 conflict */
    513       SYNC_TESTING_cmd_block_delete ("delete-block-refs-underflow",
    514                                      sync_url,
    515                                      "upload-block-refs2",
    516                                      MHD_HTTP_CONFLICT,
    517                                      1,
    518                                      obj_uids,
    519                                      ref_dec2),
    520       /* The rejected delete left the block listed ... */
    521       SYNC_TESTING_cmd_block_list ("list-after-underflow",
    522                                    sync_url,
    523                                    "upload-block-2",
    524                                    10,
    525                                    NULL,
    526                                    MHD_HTTP_OK,
    527                                    0,
    528                                    NULL,
    529                                    NULL),
    530       /* ... and the object it references is still fetchable */
    531       SYNC_TESTING_cmd_object_fetch ("fetch-obj-A-after-underflow",
    532                                      sync_url,
    533                                      "upload-block-2",
    534                                      &obj_uids[0],
    535                                      MHD_HTTP_OK,
    536                                      obj_data_A,
    537                                      sizeof obj_data_A - 1),
    538 
    539       /* ========== Object and reference error paths ========== */
    540 
    541       /* Fetching an object that was never uploaded → 404 */
    542       SYNC_TESTING_cmd_object_fetch ("fetch-obj-unknown",
    543                                      sync_url,
    544                                      "upload-block-2",
    545                                      &unknown_uid,
    546                                      MHD_HTTP_NOT_FOUND,
    547                                      NULL,
    548                                      0),
    549 
    550       /* Referencing an object that was never uploaded → 409 ... */
    551       SYNC_TESTING_cmd_block_append ("append-unknown-object-ref",
    552                                      sync_url,
    553                                      "upload-block-refs2",
    554                                      &orphan_nonce,
    555                                      MHD_HTTP_CONFLICT,
    556                                      data[0],
    557                                      strlen (data[0]),
    558                                      false,
    559                                      1,
    560                                      &unknown_uid,
    561                                      ref_inc1),
    562       /* ... and must not have moved the tail, so the next append
    563          onto upload-block-refs2 still succeeds */
    564       SYNC_TESTING_cmd_block_append ("append-after-rejected-ref",
    565                                      sync_url,
    566                                      "upload-block-refs2",
    567                                      &tail_nonce,
    568                                      MHD_HTTP_NO_CONTENT,
    569                                      data[2],
    570                                      strlen (data[2]),
    571                                      false,
    572                                      0,
    573                                      NULL,
    574                                      NULL),
    575 
    576       TALER_TESTING_cmd_end ()
    577     };
    578 
    579     TALER_TESTING_run (is,
    580                        commands);
    581   }
    582 }
    583 
    584 
    585 int
    586 main (int argc,
    587       char *const *argv)
    588 {
    589   (void) argc;
    590   payer_payto.full_payto =
    591     (char *) "payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME
    592     "?receiver-name=user";
    593   merchant_payto.full_payto =
    594     (char *) "payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME
    595     "?receiver-name=merchant";
    596   return TALER_TESTING_main (argv,
    597                              "DEBUG",
    598                              CONFIG_FILE,
    599                              "exchange-account-exchange",
    600                              TALER_TESTING_BS_FAKEBANK,
    601                              &cred,
    602                              &run,
    603                              NULL);
    604 }
    605 
    606 
    607 /* end of test_sync_api.c */