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


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