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


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