exchange

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

taler-auditor-sync.c (28712B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-2022 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-auditor-sync.c
     18  * @brief Tool used by the auditor to make a 'safe' copy of the exchanges' database.
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "exchangedb_lib.h"
     23 #include "exchange-database/preflight.h"
     24 #include "exchange-database/test_aml_officer.h"
     25 #include "exchange-database/abort_shard.h"
     26 #include "exchange-database/activate_signing_key.h"
     27 #include "exchange-database/add_denomination_key.h"
     28 #include "exchange-database/aggregate.h"
     29 #include "exchange-database/begin_revolving_shard.h"
     30 #include "exchange-database/begin_shard.h"
     31 #include "exchange-database/clear_aml_lock.h"
     32 #include "exchange-database/commit.h"
     33 #include "exchange-database/complete_shard.h"
     34 #include "exchange-database/compute_shard.h"
     35 #include "exchange-database/count_known_coins.h"
     36 #include "exchange-database/create_aggregation_transient.h"
     37 #include "exchange-database/create_tables.h"
     38 #include "exchange-database/delete_aggregation_transient.h"
     39 #include "exchange-database/delete_shard_locks.h"
     40 #include "exchange-database/disable_rules.h"
     41 #include "exchange-database/do_check_deposit_idempotent.h"
     42 #include "exchange-database/do_deposit.h"
     43 #include "exchange-database/do_purse_delete.h"
     44 #include "exchange-database/do_purse_deposit.h"
     45 #include "exchange-database/do_purse_merge.h"
     46 #include "exchange-database/do_recoup.h"
     47 #include "exchange-database/do_recoup_refresh.h"
     48 #include "exchange-database/do_refresh.h"
     49 #include "exchange-database/do_refund.h"
     50 #include "exchange-database/do_reserve_open.h"
     51 #include "exchange-database/do_reserve_purse.h"
     52 #include "exchange-database/do_withdraw.h"
     53 #include "exchange-database/drain_kyc_alert.h"
     54 #include "exchange-database/drop_tables.h"
     55 #include "exchange-database/enable_rules.h"
     56 #include "exchange-database/ensure_coin_known.h"
     57 #include "exchange-database/event_listen_cancel.h"
     58 #include "exchange-database/event_listen.h"
     59 #include "exchange-database/expire_purse.h"
     60 #include "exchange-database/find_aggregation_transient.h"
     61 #include "exchange-database/gc.h"
     62 #include "exchange-database/get_coin_denomination.h"
     63 #include "exchange-database/get_coin_transactions.h"
     64 #include "exchange-database/get_denomination_by_serial.h"
     65 #include "exchange-database/get_denomination_info.h"
     66 #include "exchange-database/get_denomination_revocation.h"
     67 #include "exchange-database/get_drain_profit.h"
     68 #include "exchange-database/get_expired_reserves.h"
     69 #include "exchange-database/get_global_fee.h"
     70 #include "exchange-database/get_global_fees.h"
     71 #include "exchange-database/get_known_coin.h"
     72 #include "exchange-database/get_kyc_rules.h"
     73 #include "exchange-database/get_old_coin_by_h_blind.h"
     74 #include "exchange-database/get_pending_kyc_requirement_process.h"
     75 #include "exchange-database/get_purse_deposit.h"
     76 #include "exchange-database/get_purse_request.h"
     77 #include "exchange-database/get_ready_deposit.h"
     78 #include "exchange-database/get_refresh.h"
     79 #include "exchange-database/get_reserve_balance.h"
     80 #include "exchange-database/get_reserve_by_h_planchets.h"
     81 #include "exchange-database/get_reserve_history.h"
     82 #include "exchange-database/get_signature_for_known_coin.h"
     83 #include "exchange-database/get_unfinished_close_requests.h"
     84 #include "exchange-database/get_wire_accounts.h"
     85 #include "exchange-database/get_wire_fee.h"
     86 #include "exchange-database/get_wire_fees.h"
     87 #include "exchange-database/get_wire_hash_for_contract.h"
     88 #include "exchange-database/get_withdraw.h"
     89 #include "exchange-database/have_deposit2.h"
     90 #include "exchange-database/inject_auditor_triggers.h"
     91 #include "exchange-database/insert_active_legitimization_measure.h"
     92 #include "exchange-database/insert_aml_decision.h"
     93 #include "exchange-database/insert_aml_officer.h"
     94 #include "exchange-database/insert_aml_program_failure.h"
     95 #include "exchange-database/insert_auditor_denom_sig.h"
     96 #include "exchange-database/insert_auditor.h"
     97 #include "exchange-database/insert_close_request.h"
     98 #include "exchange-database/insert_contract.h"
     99 #include "exchange-database/insert_denomination_info.h"
    100 #include "exchange-database/insert_denomination_revocation.h"
    101 #include "exchange-database/insert_drain_profit.h"
    102 #include "exchange-database/insert_global_fee.h"
    103 #include "exchange-database/insert_kyc_failure.h"
    104 #include "exchange-database/insert_kyc_requirement_process.h"
    105 #include "exchange-database/insert_partner.h"
    106 #include "exchange-database/insert_purse_request.h"
    107 #include "exchange-database/insert_records_by_table.h"
    108 #include "exchange-database/insert_reserve_closed.h"
    109 #include "exchange-database/insert_reserve_open_deposit.h"
    110 #include "exchange-database/insert_sanction_list_hit.h"
    111 #include "exchange-database/insert_signkey_revocation.h"
    112 #include "exchange-database/insert_successor_measure.h"
    113 #include "exchange-database/insert_wire_fee.h"
    114 #include "exchange-database/insert_wire.h"
    115 #include "exchange-database/iterate_active_auditors.h"
    116 #include "exchange-database/iterate_active_signkeys.h"
    117 #include "exchange-database/iterate_auditor_denominations.h"
    118 #include "exchange-database/iterate_denomination_info.h"
    119 #include "exchange-database/iterate_denominations.h"
    120 #include "exchange-database/iterate_kyc_reference.h"
    121 #include "exchange-database/iterate_reserve_close_info.h"
    122 #include "exchange-database/kycauth_in_insert.h"
    123 #include "exchange-database/kyc_provider_account_lookup.h"
    124 #include "exchange-database/lookup_active_legitimization.h"
    125 #include "exchange-database/lookup_aml_file_number.h"
    126 #include "exchange-database/lookup_aml_history.h"
    127 #include "exchange-database/lookup_aml_officer.h"
    128 #include "exchange-database/lookup_auditor_status.h"
    129 #include "exchange-database/lookup_auditor_timestamp.h"
    130 #include "exchange-database/lookup_completed_legitimization.h"
    131 #include "exchange-database/lookup_denomination_key.h"
    132 #include "exchange-database/lookup_global_fee_by_time.h"
    133 #include "exchange-database/lookup_h_payto_by_access_token.h"
    134 #include "exchange-database/lookup_kyc_history.h"
    135 #include "exchange-database/lookup_kyc_process_by_account.h"
    136 #include "exchange-database/lookup_kyc_requirement_by_row.h"
    137 #include "exchange-database/lookup_kyc_status_by_token.h"
    138 #include "exchange-database/lookup_pending_legitimization.h"
    139 #include "exchange-database/lookup_records_by_table.h"
    140 #include "exchange-database/lookup_rules_by_access_token.h"
    141 #include "exchange-database/lookup_serial_by_table.h"
    142 #include "exchange-database/lookup_signing_key.h"
    143 #include "exchange-database/lookup_signkey_revocation.h"
    144 #include "exchange-database/lookup_transfer_by_deposit.h"
    145 #include "exchange-database/lookup_wire_fee_by_time.h"
    146 #include "exchange-database/lookup_wire_timestamp.h"
    147 #include "exchange-database/lookup_wire_transfer.h"
    148 #include "exchange-database/mark_refresh_reveal_success.h"
    149 #include "exchange-database/persist_kyc_attributes.h"
    150 #include "exchange-database/preflight.h"
    151 #include "exchange-database/profit_drains_get_pending.h"
    152 #include "exchange-database/profit_drains_set_finished.h"
    153 #include "exchange-database/release_revolving_shard.h"
    154 #include "exchange-database/reserves_get.h"
    155 #include "exchange-database/reserves_get_origin.h"
    156 #include "exchange-database/reserves_in_insert.h"
    157 #include "exchange-database/reserves_update.h"
    158 #include "exchange-database/rollback.h"
    159 #include "exchange-database/select_account_merges_above_serial_id.h"
    160 #include "exchange-database/select_aggregation_amounts_for_kyc_check.h"
    161 #include "exchange-database/select_aggregations_above_serial.h"
    162 #include "exchange-database/select_aggregation_transient.h"
    163 #include "exchange-database/select_all_kyc_attributes.h"
    164 #include "exchange-database/select_all_purse_decisions_above_serial_id.h"
    165 #include "exchange-database/select_all_purse_deletions_above_serial_id.h"
    166 #include "exchange-database/select_aml_attributes.h"
    167 #include "exchange-database/select_aml_decisions.h"
    168 #include "exchange-database/select_aml_measures.h"
    169 #include "exchange-database/select_aml_statistics.h"
    170 #include "exchange-database/select_auditor_denom_sig.h"
    171 #include "exchange-database/select_batch_deposits_missing_wire.h"
    172 #include "exchange-database/select_coin_deposits_above_serial_id.h"
    173 #include "exchange-database/select_contract_by_purse.h"
    174 #include "exchange-database/select_contract.h"
    175 #include "exchange-database/select_deposit_amounts_for_kyc_check.h"
    176 #include "exchange-database/select_exchange_credit_transfers.h"
    177 #include "exchange-database/select_exchange_debit_transfers.h"
    178 #include "exchange-database/select_exchange_kycauth_transfers.h"
    179 #include "exchange-database/select_kyc_accounts.h"
    180 #include "exchange-database/select_kyc_attributes.h"
    181 #include "exchange-database/select_merge_amounts_for_kyc_check.h"
    182 #include "exchange-database/select_purse_by_merge_pub.h"
    183 #include "exchange-database/select_purse_decisions_above_serial_id.h"
    184 #include "exchange-database/select_purse_deposits_above_serial_id.h"
    185 #include "exchange-database/select_purse_deposits_by_purse.h"
    186 #include "exchange-database/select_purse.h"
    187 #include "exchange-database/select_purse_merge.h"
    188 #include "exchange-database/select_purse_merges_above_serial_id.h"
    189 #include "exchange-database/select_purse_requests_above_serial_id.h"
    190 #include "exchange-database/select_recoup_above_serial_id.h"
    191 #include "exchange-database/select_recoup_refresh_above_serial_id.h"
    192 #include "exchange-database/select_refreshes_above_serial_id.h"
    193 #include "exchange-database/select_refunds_above_serial_id.h"
    194 #include "exchange-database/select_refunds_by_coin.h"
    195 #include "exchange-database/select_reserve_closed_above_serial_id.h"
    196 #include "exchange-database/select_reserve_close_info.h"
    197 #include "exchange-database/select_reserve_open_above_serial_id.h"
    198 #include "exchange-database/select_reserves_in_above_serial_id.h"
    199 #include "exchange-database/select_wire_out_above_serial_id_by_account.h"
    200 #include "exchange-database/select_wire_out_above_serial_id.h"
    201 #include "exchange-database/select_withdrawals_above_serial_id.h"
    202 #include "exchange-database/select_withdraw_amounts_for_kyc_check.h"
    203 #include "exchange-database/set_aml_lock.h"
    204 #include "exchange-database/set_purse_balance.h"
    205 #include "exchange-database/start_deferred_wire_out.h"
    206 #include "exchange-database/start.h"
    207 #include "exchange-database/start_read_committed.h"
    208 #include "exchange-database/start_read_only.h"
    209 #include "exchange-database/store_wire_transfer_out.h"
    210 #include "exchange-database/test_aml_officer.h"
    211 #include "exchange-database/trigger_kyc_rule_for_account.h"
    212 #include "exchange-database/update_aggregation_transient.h"
    213 #include "exchange-database/update_auditor.h"
    214 #include "exchange-database/update_kyc_process_by_row.h"
    215 #include "exchange-database/update_wire.h"
    216 #include "exchange-database/wad_in_insert.h"
    217 #include "exchange-database/wire_prepare_data_get.h"
    218 #include "exchange-database/wire_prepare_data_insert.h"
    219 #include "exchange-database/wire_prepare_data_mark_failed.h"
    220 #include "exchange-database/wire_prepare_data_mark_finished.h"
    221 
    222 
    223 /**
    224  * Handle to access the exchange's source database.
    225  */
    226 static struct TALER_EXCHANGEDB_PostgresContext *src;
    227 
    228 /**
    229  * Handle to access the exchange's destination database.
    230  */
    231 static struct TALER_EXCHANGEDB_PostgresContext *dst;
    232 
    233 /**
    234  * Return value from #main().
    235  */
    236 static int global_ret;
    237 
    238 /**
    239  * Main task to do synchronization.
    240  */
    241 static struct GNUNET_SCHEDULER_Task *sync_task;
    242 
    243 /**
    244  * What is our target transaction size (number of records)?
    245  */
    246 static unsigned int transaction_size = 512;
    247 
    248 /**
    249  * Number of records copied in this transaction.
    250  */
    251 static unsigned long long actual_size;
    252 
    253 /**
    254  * Terminate once synchronization is achieved.
    255  */
    256 static int exit_if_synced;
    257 
    258 
    259 /**
    260  * Information we track per replicated table.
    261  */
    262 struct Table
    263 {
    264   /**
    265    * Which table is this record about?
    266    */
    267   enum TALER_EXCHANGEDB_ReplicatedTable rt;
    268 
    269   /**
    270    * Up to which record is the destination table synchronized.
    271    */
    272   uint64_t start_serial;
    273 
    274   /**
    275    * Highest serial in the source table.
    276    */
    277   uint64_t end_serial;
    278 
    279   /**
    280    * Marker for the end of the list of #tables.
    281    */
    282   bool end;
    283 };
    284 
    285 
    286 /**
    287  * Information about replicated tables.
    288  */
    289 static struct Table tables[] = {
    290   { .rt = TALER_EXCHANGEDB_RT_DENOMINATIONS},
    291   { .rt = TALER_EXCHANGEDB_RT_DENOMINATION_REVOCATIONS},
    292   { .rt = TALER_EXCHANGEDB_RT_KYC_TARGETS},
    293   { .rt = TALER_EXCHANGEDB_RT_WIRE_TARGETS},
    294   { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_MEASURES},
    295   { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_OUTCOMES},
    296   { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_PROCESSES},
    297   { .rt = TALER_EXCHANGEDB_RT_RESERVES},
    298   { .rt = TALER_EXCHANGEDB_RT_RESERVES_IN},
    299   { .rt = TALER_EXCHANGEDB_RT_RESERVES_CLOSE},
    300   { .rt = TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS},
    301   { .rt = TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS},
    302   { .rt = TALER_EXCHANGEDB_RT_WITHDRAW},
    303   { .rt = TALER_EXCHANGEDB_RT_AUDITORS},
    304   { .rt = TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS},
    305   { .rt = TALER_EXCHANGEDB_RT_EXCHANGE_SIGN_KEYS},
    306   { .rt = TALER_EXCHANGEDB_RT_SIGNKEY_REVOCATIONS},
    307   { .rt = TALER_EXCHANGEDB_RT_KNOWN_COINS},
    308   { .rt = TALER_EXCHANGEDB_RT_REFRESH},
    309   { .rt = TALER_EXCHANGEDB_RT_BATCH_DEPOSITS},
    310   { .rt = TALER_EXCHANGEDB_RT_COIN_DEPOSITS},
    311   { .rt = TALER_EXCHANGEDB_RT_REFUNDS},
    312   { .rt = TALER_EXCHANGEDB_RT_WIRE_OUT},
    313   { .rt = TALER_EXCHANGEDB_RT_AGGREGATION_TRACKING},
    314   { .rt = TALER_EXCHANGEDB_RT_WIRE_FEE},
    315   { .rt = TALER_EXCHANGEDB_RT_GLOBAL_FEE},
    316   { .rt = TALER_EXCHANGEDB_RT_RECOUP},
    317   { .rt = TALER_EXCHANGEDB_RT_RECOUP_REFRESH },
    318   { .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS},
    319   { .rt = TALER_EXCHANGEDB_RT_PURSE_DECISION},
    320   { .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES},
    321   { .rt = TALER_EXCHANGEDB_RT_PURSE_DEPOSITS},
    322   { .rt = TALER_EXCHANGEDB_RT_ACCOUNT_MERGES},
    323   { .rt = TALER_EXCHANGEDB_RT_HISTORY_REQUESTS},
    324   { .rt = TALER_EXCHANGEDB_RT_CLOSE_REQUESTS},
    325   { .rt = TALER_EXCHANGEDB_RT_WADS_OUT},
    326   { .rt = TALER_EXCHANGEDB_RT_WADS_OUT_ENTRIES},
    327   { .rt = TALER_EXCHANGEDB_RT_WADS_IN},
    328   { .rt = TALER_EXCHANGEDB_RT_WADS_IN_ENTRIES},
    329   { .rt = TALER_EXCHANGEDB_RT_PROFIT_DRAINS},
    330   { .end = true }
    331 };
    332 
    333 
    334 /**
    335  * Closure for #do_insert.
    336  */
    337 struct InsertContext
    338 {
    339   /**
    340    * Table we are replicating.
    341    */
    342   struct Table *table;
    343 
    344   /**
    345    * Set to error if insertion created an error.
    346    */
    347   enum GNUNET_DB_QueryStatus qs;
    348 };
    349 
    350 
    351 /**
    352  * Function called on data to replicate in the auditor's database.
    353  *
    354  * @param cls closure, a `struct InsertContext`
    355  * @param td record from an exchange table
    356  * @return #GNUNET_OK to continue to iterate,
    357  *         #GNUNET_SYSERR to fail with an error
    358  */
    359 static enum GNUNET_GenericReturnValue
    360 do_insert (void *cls,
    361            const struct TALER_EXCHANGEDB_TableData *td)
    362 {
    363   struct InsertContext *ctx = cls;
    364   enum GNUNET_DB_QueryStatus qs;
    365 
    366   if (0 >= ctx->qs)
    367     return GNUNET_SYSERR;
    368   qs = TALER_EXCHANGEDB_insert_records_by_table (dst,
    369                                                  td);
    370   if (0 >= qs)
    371   {
    372     switch (qs)
    373     {
    374     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    375       GNUNET_assert (0);
    376       break;
    377     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    378       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    379                   "Failed to insert record into table %d: no change\n",
    380                   td->table);
    381       break;
    382     case GNUNET_DB_STATUS_SOFT_ERROR:
    383       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    384                   "Serialization error inserting record into table %d (will retry)\n",
    385                   td->table);
    386       break;
    387     case GNUNET_DB_STATUS_HARD_ERROR:
    388       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    389                   "Failed to insert record into table %d: hard error\n",
    390                   td->table);
    391       break;
    392     }
    393     ctx->qs = qs;
    394     return GNUNET_SYSERR;
    395   }
    396   actual_size++;
    397   ctx->table->start_serial = td->serial;
    398   return GNUNET_OK;
    399 }
    400 
    401 
    402 /**
    403  * Run one replication transaction.
    404  *
    405  * @return #GNUNET_OK on success, #GNUNET_SYSERR to rollback
    406  */
    407 static enum GNUNET_GenericReturnValue
    408 transact (void)
    409 {
    410   struct InsertContext ctx = {
    411     .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    412   };
    413 
    414   if (0 >
    415       TALER_EXCHANGEDB_start (src,
    416                               "lookup src serials"))
    417     return GNUNET_SYSERR;
    418   for (unsigned int i = 0; ! tables[i].end; i++)
    419     TALER_EXCHANGEDB_lookup_serial_by_table (src,
    420                                              tables[i].rt,
    421                                              &tables[i].end_serial);
    422   TALER_EXCHANGEDB_rollback (src);
    423   if (GNUNET_OK !=
    424       TALER_EXCHANGEDB_start (dst,
    425                               "lookup dst serials"))
    426     return GNUNET_SYSERR;
    427   for (unsigned int i = 0; ! tables[i].end; i++)
    428     TALER_EXCHANGEDB_lookup_serial_by_table (dst,
    429                                              tables[i].rt,
    430                                              &tables[i].start_serial);
    431   TALER_EXCHANGEDB_rollback (dst);
    432   for (unsigned int i = 0; ! tables[i].end; i++)
    433   {
    434     struct Table *table = &tables[i];
    435 
    436     if (table->start_serial == table->end_serial)
    437       continue;
    438     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    439                 "Replicating table %d from %llu to %llu\n",
    440                 i,
    441                 (unsigned long long) table->start_serial,
    442                 (unsigned long long) table->end_serial);
    443     ctx.table = table;
    444     while (table->start_serial < table->end_serial)
    445     {
    446       enum GNUNET_DB_QueryStatus qs;
    447 
    448       if (GNUNET_OK !=
    449           TALER_EXCHANGEDB_start (src,
    450                                   "copy table (src)"))
    451         return GNUNET_SYSERR;
    452       if (GNUNET_OK !=
    453           TALER_EXCHANGEDB_start (dst,
    454                                   "copy table (dst)"))
    455         return GNUNET_SYSERR;
    456       qs = TALER_EXCHANGEDB_lookup_records_by_table (src,
    457                                                      table->rt,
    458                                                      table->start_serial,
    459                                                      &do_insert,
    460                                                      &ctx);
    461       if (ctx.qs < 0)
    462         qs = ctx.qs;
    463       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    464       {
    465         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    466                     "Failed to lookup records from table %d: hard error\n",
    467                     i);
    468         global_ret = EXIT_FAILURE;
    469         return GNUNET_SYSERR;
    470       }
    471       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    472       {
    473         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    474                     "Serialization error looking up records from table %d (will retry)\n",
    475                     i);
    476         return GNUNET_SYSERR; /* will retry */
    477       }
    478       if (0 == qs)
    479       {
    480         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    481                     "Failed to lookup records from table %d: no results\n",
    482                     i);
    483         GNUNET_break (0); /* should be impossible */
    484         global_ret = EXIT_FAILURE;
    485         return GNUNET_SYSERR;
    486       }
    487       if (0 == ctx.qs)
    488         return GNUNET_SYSERR; /* insertion failed, maybe record existed? try again */
    489       TALER_EXCHANGEDB_rollback (src);
    490       qs = TALER_EXCHANGEDB_commit (dst);
    491       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    492       {
    493         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    494                     "Serialization error committing transaction on table %d (will retry)\n",
    495                     i);
    496         continue;
    497       }
    498       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    499       {
    500         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    501                     "Hard error committing transaction on table %d\n",
    502                     i);
    503         global_ret = EXIT_FAILURE;
    504         return GNUNET_SYSERR;
    505       }
    506     }
    507   }
    508   /* we do not care about conflicting UPDATEs to src table, so safe to just rollback */
    509   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    510               "Sync pass completed successfully with %llu updates\n",
    511               actual_size);
    512   return GNUNET_OK;
    513 }
    514 
    515 
    516 /**
    517  * Task to do the actual synchronization work.
    518  *
    519  * @param cls NULL, unused
    520  */
    521 static void
    522 do_sync (void *cls)
    523 {
    524   static struct GNUNET_TIME_Relative delay;
    525 
    526   (void) cls;
    527   sync_task = NULL;
    528   actual_size = 0;
    529   if (GNUNET_SYSERR ==
    530       TALER_EXCHANGEDB_preflight (src))
    531   {
    532     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    533                 "Failed to begin transaction with data source. Exiting\n");
    534     return;
    535   }
    536   if (GNUNET_SYSERR ==
    537       TALER_EXCHANGEDB_preflight (dst))
    538   {
    539     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    540                 "Failed to begin transaction with data destination. Exiting\n");
    541     return;
    542   }
    543   if (GNUNET_OK != transact ())
    544   {
    545     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    546                 "Transaction failed, rolling back\n");
    547     TALER_EXCHANGEDB_rollback (src);
    548     TALER_EXCHANGEDB_rollback (dst);
    549   }
    550   if (0 != global_ret)
    551   {
    552     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    553                 "Transaction failed permanently, exiting\n");
    554     return;
    555   }
    556   if ( (0 == actual_size) &&
    557        (exit_if_synced) )
    558   {
    559     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    560                 "Databases are synchronized. Exiting\n");
    561     return;
    562   }
    563   if (actual_size < transaction_size / 2)
    564   {
    565     delay = GNUNET_TIME_STD_BACKOFF (delay);
    566   }
    567   else if (actual_size >= transaction_size)
    568   {
    569     delay = GNUNET_TIME_UNIT_ZERO;
    570   }
    571   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    572               "Next sync pass in %s\n",
    573               GNUNET_STRINGS_relative_time_to_string (delay,
    574                                                       GNUNET_YES));
    575   sync_task = GNUNET_SCHEDULER_add_delayed (delay,
    576                                             &do_sync,
    577                                             NULL);
    578 }
    579 
    580 
    581 /**
    582  * Set an option of type 'char *' from the command line with
    583  * filename expansion a la #GNUNET_STRINGS_filename_expand().
    584  *
    585  * @param ctx command line processing context
    586  * @param scls additional closure (will point to the `char *`,
    587  *             which will be allocated)
    588  * @param option name of the option
    589  * @param value actual value of the option (a string)
    590  * @return #GNUNET_OK
    591  */
    592 static enum GNUNET_GenericReturnValue
    593 set_filename (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
    594               void *scls,
    595               const char *option,
    596               const char *value)
    597 {
    598   char **val = scls;
    599 
    600   (void) ctx;
    601   (void) option;
    602   GNUNET_assert (NULL != value);
    603   GNUNET_free (*val);
    604   *val = GNUNET_STRINGS_filename_expand (value);
    605   return GNUNET_OK;
    606 }
    607 
    608 
    609 /**
    610  * Allow user to specify configuration file name (-s option)
    611  *
    612  * @param[out] fn set to the name of the configuration file
    613  */
    614 static struct GNUNET_GETOPT_CommandLineOption
    615 option_cfgfile_src (char **fn)
    616 {
    617   struct GNUNET_GETOPT_CommandLineOption clo = {
    618     .shortName = 's',
    619     .name = "source-configuration",
    620     .argumentHelp = "FILENAME",
    621     .description = gettext_noop (
    622       "use configuration file FILENAME for the SOURCE database"),
    623     .require_argument = 1,
    624     .processor = &set_filename,
    625     .scls = (void *) fn
    626   };
    627 
    628   return clo;
    629 }
    630 
    631 
    632 /**
    633  * Allow user to specify configuration file name (-d option)
    634  *
    635  * @param[out] fn set to the name of the configuration file
    636  */
    637 static struct GNUNET_GETOPT_CommandLineOption
    638 option_cfgfile_dst (char **fn)
    639 {
    640   struct GNUNET_GETOPT_CommandLineOption clo = {
    641     .shortName = 'd',
    642     .name = "destination-configuration",
    643     .argumentHelp = "FILENAME",
    644     .description = gettext_noop (
    645       "use configuration file FILENAME for the DESTINATION database"),
    646     .require_argument = 1,
    647     .processor = &set_filename,
    648     .scls = (void *) fn
    649   };
    650 
    651   return clo;
    652 }
    653 
    654 
    655 static struct GNUNET_CONFIGURATION_Handle *
    656 load_config (const char *cfgfile)
    657 {
    658   struct GNUNET_CONFIGURATION_Handle *cfg;
    659 
    660   cfg = GNUNET_CONFIGURATION_create (TALER_AUDITOR_project_data ());
    661   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    662               "Loading config file: %s\n",
    663               cfgfile);
    664   if (GNUNET_SYSERR ==
    665       GNUNET_CONFIGURATION_load (cfg,
    666                                  cfgfile))
    667   {
    668     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    669                 "Malformed configuration file `%s', exit ...\n",
    670                 cfgfile);
    671     GNUNET_CONFIGURATION_destroy (cfg);
    672     return NULL;
    673   }
    674   return cfg;
    675 }
    676 
    677 
    678 /**
    679  * Shutdown task.
    680  *
    681  * @param cls NULL, unused
    682  */
    683 static void
    684 do_shutdown (void *cls)
    685 {
    686   (void) cls;
    687   if (NULL != sync_task)
    688   {
    689     GNUNET_SCHEDULER_cancel (sync_task);
    690     sync_task = NULL;
    691   }
    692 }
    693 
    694 
    695 /**
    696  * Initial task.
    697  *
    698  * @param cls NULL, unused
    699  */
    700 static void
    701 run (void *cls)
    702 {
    703   (void) cls;
    704 
    705   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    706                                  NULL);
    707   sync_task = GNUNET_SCHEDULER_add_now (&do_sync,
    708                                         NULL);
    709 }
    710 
    711 
    712 /**
    713  * Setup plugins in #src and #dst and #run() the main
    714  * logic with those plugins.
    715  */
    716 static void
    717 setup (struct GNUNET_CONFIGURATION_Handle *src_cfg,
    718        struct GNUNET_CONFIGURATION_Handle *dst_cfg)
    719 {
    720   src = TALER_EXCHANGEDB_connect (src_cfg,
    721                                   false);
    722   if (NULL == src)
    723   {
    724     global_ret = EXIT_NOTINSTALLED;
    725     return;
    726   }
    727   dst = TALER_EXCHANGEDB_connect (dst_cfg,
    728                                   false);
    729   if (NULL == dst)
    730   {
    731     global_ret = EXIT_NOTINSTALLED;
    732     TALER_EXCHANGEDB_disconnect (src);
    733     src = NULL;
    734     return;
    735   }
    736   GNUNET_SCHEDULER_run (&run,
    737                         NULL);
    738   TALER_EXCHANGEDB_disconnect (src);
    739   src = NULL;
    740   TALER_EXCHANGEDB_disconnect (dst);
    741   dst = NULL;
    742 }
    743 
    744 
    745 /**
    746  * The main function of the taler-auditor-exchange tool.  This tool is used
    747  * to add (or remove) an exchange's master key and base URL to the auditor's
    748  * database.
    749  *
    750  * @param argc number of arguments from the command line
    751  * @param argv command line arguments
    752  * @return 0 ok, non-zero on error
    753  */
    754 int
    755 main (int argc,
    756       char *const *argv)
    757 {
    758   char *src_cfgfile = NULL;
    759   char *dst_cfgfile = NULL;
    760   char *level = GNUNET_strdup ("WARNING");
    761   struct GNUNET_CONFIGURATION_Handle *src_cfg;
    762   struct GNUNET_CONFIGURATION_Handle *dst_cfg;
    763   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    764     GNUNET_GETOPT_option_mandatory (
    765       option_cfgfile_src (&src_cfgfile)),
    766     GNUNET_GETOPT_option_mandatory (
    767       option_cfgfile_dst (&dst_cfgfile)),
    768     GNUNET_GETOPT_option_help (
    769       TALER_AUDITOR_project_data (),
    770       gettext_noop ("Make a safe copy of an exchange database")),
    771     GNUNET_GETOPT_option_uint (
    772       'b',
    773       "batch",
    774       "SIZE",
    775       gettext_noop (
    776         "target SIZE for a the number of records to copy in one transaction"),
    777       &transaction_size),
    778     GNUNET_GETOPT_option_flag (
    779       't',
    780       "terminate-when-synchronized",
    781       gettext_noop (
    782         "terminate as soon as the databases are synchronized"),
    783       &exit_if_synced),
    784     GNUNET_GETOPT_option_version (VERSION),
    785     GNUNET_GETOPT_option_loglevel (&level),
    786     GNUNET_GETOPT_OPTION_END
    787   };
    788 
    789   TALER_gcrypt_init (); /* must trigger initialization manually at this point! */
    790   {
    791     int ret;
    792 
    793     ret = GNUNET_GETOPT_run ("taler-auditor-sync",
    794                              options,
    795                              argc, argv);
    796     if (GNUNET_NO == ret)
    797       return EXIT_SUCCESS;
    798     if (GNUNET_SYSERR == ret)
    799       return EXIT_INVALIDARGUMENT;
    800   }
    801   GNUNET_assert (GNUNET_OK ==
    802                  GNUNET_log_setup ("taler-auditor-sync",
    803                                    level,
    804                                    NULL));
    805   GNUNET_free (level);
    806   /* suppress compiler warnings... */
    807   GNUNET_assert (NULL != src_cfgfile);
    808   GNUNET_assert (NULL != dst_cfgfile);
    809   if (0 == strcmp (src_cfgfile,
    810                    dst_cfgfile))
    811   {
    812     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    813                 "Source and destination configuration files must differ!\n");
    814     return EXIT_INVALIDARGUMENT;
    815   }
    816   src_cfg = load_config (src_cfgfile);
    817   if (NULL == src_cfg)
    818   {
    819     GNUNET_free (src_cfgfile);
    820     GNUNET_free (dst_cfgfile);
    821     return EXIT_NOTCONFIGURED;
    822   }
    823   dst_cfg = load_config (dst_cfgfile);
    824   if (NULL == dst_cfg)
    825   {
    826     GNUNET_CONFIGURATION_destroy (src_cfg);
    827     GNUNET_free (src_cfgfile);
    828     GNUNET_free (dst_cfgfile);
    829     return EXIT_NOTCONFIGURED;
    830   }
    831   setup (src_cfg,
    832          dst_cfg);
    833   GNUNET_CONFIGURATION_destroy (src_cfg);
    834   GNUNET_CONFIGURATION_destroy (dst_cfg);
    835   GNUNET_free (src_cfgfile);
    836   GNUNET_free (dst_cfgfile);
    837 
    838   return global_ret;
    839 }
    840 
    841 
    842 /* end of taler-auditor-sync.c */