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


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