diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/auditor/taler-auditor.c | 227 | ||||
| -rw-r--r-- | src/auditordb/plugin_auditordb_postgres.c | 159 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_deposit.c | 3 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 24 | ||||
| -rw-r--r-- | src/include/taler_auditordb_plugin.h | 35 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 4 | 
6 files changed, 435 insertions, 17 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index 8c98c3c2..c1aaa614 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -183,6 +183,11 @@ static struct TALER_Amount total_balance_reserve_not_closed;  static json_t *report_wire_out_inconsistencies;  /** + * Array of reports about missing deposit confirmations. + */ +static json_t *report_deposit_confirmation_inconsistencies; + +/**   * Total delta between calculated and stored wire out transfers,   * for positive deltas.   */ @@ -236,6 +241,16 @@ static struct TALER_Amount total_arithmetic_delta_plus;  static struct TALER_Amount total_arithmetic_delta_minus;  /** + * Total number of deposit confirmations that we did not get. + */ +static json_int_t number_missed_deposit_confirmations; + +/** + * Total amount involved in deposit confirmations that we did not get. + */ +static struct TALER_Amount total_missed_deposit_confirmations; + +/**   * Total amount reported in all calls to #report_emergency().   */  static struct TALER_Amount reported_emergency_sum; @@ -4036,6 +4051,202 @@ analyze_coins (void *cls)  } +/* *************************** Analysis of deposit-confirmations ********** */ + +/** + * Closure for #test_dc. + */ +struct DepositConfirmationContext +{ + +  /** +   * How many deposit confirmations did we NOT find in the #edb? +   */ +  unsigned long long missed_count; + +  /** +   * What is the total amount missing? +   */ +  struct TALER_Amount missed_amount; + +  /** +   * Lowest SerialID of the first coin we missed? (This is where we +   * should resume next time). +   */ +  uint64_t first_missed_coin_serial; + +  /** +   * Lowest SerialID of the first coin we missed? (This is where we +   * should resume next time). +   */ +  uint64_t last_seen_coin_serial; + +  /** +   * Success or failure of (exchange) database operations within +   * #test_dc. +   */ +  enum GNUNET_DB_QueryStatus qs; + +}; + + +/** + * Given a deposit confirmation from #adb, check that it is also + * in #edb.  Update the deposit confirmation context accordingly. + * + * @param cls our `struct DepositConfirmationContext` + * @param serial_id row of the @a dc in the database + * @param dc the deposit confirmation we know + */ +static void +test_dc (void *cls, +         uint64_t serial_id, +         const struct TALER_AUDITORDB_DepositConfirmation *dc) +{ +  struct DepositConfirmationContext *dcc = cls; +  enum GNUNET_DB_QueryStatus qs; +  struct TALER_EXCHANGEDB_Deposit dep; + +  dcc->last_seen_coin_serial = serial_id; +  memset (&dep, +          0, +          sizeof (dep)); +  dep.coin.coin_pub = dc->coin_pub; +  dep.h_contract_terms = dc->h_contract_terms; +  dep.merchant_pub = dc->merchant; +  dep.h_wire = dc->h_wire; +  dep.refund_deadline = dc->refund_deadline; + +  qs = edb->have_deposit (edb->cls, +                          esession, +                          &dep, +                          GNUNET_NO /* do not check refund deadline */); +  if (qs > 0) +    return; /* found, all good */ +  if (qs < 0) +  { +    GNUNET_break (0); /* DB error, complain */ +    dcc->qs = qs; +    return; +  } +  /* deposit confirmation missing! report! */ +  report (report_deposit_confirmation_inconsistencies, +          json_pack ("{s:s, s:o, s:I, s:o}", +                     "timestamp", +                     GNUNET_STRINGS_absolute_time_to_string (dc->timestamp), +                     "amount", +                     TALER_JSON_from_amount (&dc->amount_without_fee), +                     "rowid", +                     (json_int_t) serial_id, +                     "account", +                     GNUNET_JSON_from_data_auto (&dc->h_wire))); +  dcc->first_missed_coin_serial = GNUNET_MIN (dcc->first_missed_coin_serial, +                                              serial_id); +  dcc->missed_count++; +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_add (&dcc->missed_amount, +                                   &dcc->missed_amount, +                                   &dc->amount_without_fee)); +} + + +/** + * Check that the deposit-confirmations that were reported to + * us by merchants are also in the exchange's database. + * + * @param cls closure + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +analyze_deposit_confirmations (void *cls) +{ +  struct TALER_AUDITORDB_ProgressPointDepositConfirmation ppdc; +  struct DepositConfirmationContext dcc; +  enum GNUNET_DB_QueryStatus qs; +  enum GNUNET_DB_QueryStatus qsx; +  enum GNUNET_DB_QueryStatus qsp; + +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, +              "Analyzing deposit confirmations\n"); +  ppdc.last_deposit_confirmation_serial_id = 0; +  qsp = adb->get_auditor_progress_deposit_confirmation (adb->cls, +                                                        asession, +                                                        &master_pub, +                                                        &ppdc); +  if (0 > qsp) +  { +    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsp); +    return qsp; +  } +  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsp) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, +                _("First analysis using this auditor, starting audit from scratch\n")); +  } +  else +  { +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                _("Resuming deposit confirmation audit at %llu\n"), +                (unsigned long long) ppdc.last_deposit_confirmation_serial_id); +  } + +  /* setup 'cc' */ +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_get_zero (currency, +                                        &dcc.missed_amount)); +  dcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +  dcc.missed_count = 0LLU; +  dcc.first_missed_coin_serial = UINT64_MAX; +  qsx = adb->get_deposit_confirmations (adb->cls, +                                        asession, +                                        &master_pub, +                                        ppdc.last_deposit_confirmation_serial_id, +                                        &test_dc, +                                        &dcc); +  if (0 > qsx) +  { +    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); +    return qsx; +  } +  if (0 > dcc.qs) +  { +    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == dcc.qs); +    return dcc.qs; +  } +  if (UINT64_MAX == dcc.first_missed_coin_serial) +    ppdc.last_deposit_confirmation_serial_id = dcc.last_seen_coin_serial; +  else +    ppdc.last_deposit_confirmation_serial_id = dcc.first_missed_coin_serial - 1; + +  /* sync 'cc' back to disk */ +  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp) +    qs = adb->update_auditor_progress_deposit_confirmation (adb->cls, +                                                            asession, +                                                            &master_pub, +                                                            &ppdc); +  else +    qs = adb->insert_auditor_progress_deposit_confirmation (adb->cls, +                                                            asession, +                                                            &master_pub, +                                                            &ppdc); +  if (0 >= qs) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +		"Failed to update auditor DB, not recording progress\n"); +    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); +    return qs; +  } +  number_missed_deposit_confirmations = (json_int_t) dcc.missed_count; +  total_missed_deposit_confirmations = dcc.missed_amount; + +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              _("Concluded deposit confirmation audit step at %llu\n"), +              (unsigned long long) ppdc.last_deposit_confirmation_serial_id); +  return qs; +} + + +  /* *************************** General transaction logic ****************** */  /** @@ -4151,6 +4362,8 @@ setup_sessions_and_run ()              NULL);    transact (&analyze_coins,              NULL); +  transact (&analyze_deposit_confirmations, +            NULL);  } @@ -4348,6 +4561,8 @@ run (void *cls,    GNUNET_assert (NULL !=  		 (report_wire_out_inconsistencies = json_array ()));    GNUNET_assert (NULL != +		 (report_deposit_confirmation_inconsistencies = json_array ())); +  GNUNET_assert (NULL !=  		 (report_coin_inconsistencies = json_array ()));    GNUNET_assert (NULL !=  		 (report_aggregation_fee_balances = json_array ())); @@ -4383,7 +4598,8 @@ run (void *cls,                        " s:o, s:o, s:o, s:o, s:o,"                        " s:o, s:o, s:o, s:o, s:o,"                        " s:o, s:o, s:o, s:o, s:o," -                      " s:o, s:o, s:o }", +                      " s:o, s:o, s:o, s:o, s:I," +                      " s:o }",                        /* blocks of 5 for easier counting/matching to format string */                        /* block */  		      "reserve_balance_insufficient_inconsistencies", @@ -4457,7 +4673,14 @@ run (void *cls,                        "total_refresh_hanging",                        TALER_JSON_from_amount (&total_refresh_hanging),                        "refresh_hanging", -                      report_refreshs_hanging); +                      report_refreshs_hanging, +                      "deposit_confirmation_inconsistencies", +                      report_deposit_confirmation_inconsistencies, +                      "missing_deposit_confirmation_count", +                      (json_int_t) number_missed_deposit_confirmations, +                      /* block */ +                      "missing_deposit_confirmation_total", +                      TALER_JSON_from_amount (&total_missed_deposit_confirmations));    GNUNET_break (NULL != report);    json_dumpf (report,  	      stdout, diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c index 6f2aa290..aeb96df5 100644 --- a/src/auditordb/plugin_auditordb_postgres.c +++ b/src/auditordb/plugin_auditordb_postgres.c @@ -390,6 +390,7 @@ postgres_create_tables (void *cls)         we must check that the exchange reported these properly. */      GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS deposit_confirmations "  			    "(master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE" +                            ",serial_id BIGSERIAL UNIQUE"  			    ",h_contract_terms BYTEA CHECK (LENGTH(h_contract_terms)=64)"                              ",h_wire BYTEA CHECK (LENGTH(h_wire)=64)"  			    ",timestamp INT8 NOT NULL" @@ -567,6 +568,25 @@ postgres_prepare (PGconn *db_conn)  			    ",master_sig" /* master_sig could be normalized... */  			    ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);",  			    11), +    /* Used in #postgres_get_deposit_confirmations() */ +    GNUNET_PQ_make_prepare ("auditor_deposit_confirmation_select", +			    "SELECT" +			    " h_contract_terms" +			    ",h_wire" +			    ",timestamp" +			    ",refund_deadline" +			    ",amount_without_fee_val" +			    ",amount_without_fee_frac" +			    ",amount_without_fee_curr" +			    ",coin_pub" +			    ",merchant_pub" +			    ",exchange_sig" +			    ",exchange_pub" +			    ",master_sig" /* master_sig could be normalized... */ +                            " FROM deposit_confirmations" +                            " WHERE master_pub=$1" +                            " AND serial_id>$2", +			    2),      /* Used in #postgres_update_auditor_progress_reserve() */      GNUNET_PQ_make_prepare ("auditor_progress_update_reserve",  			    "UPDATE auditor_progress_reserve SET " @@ -1417,6 +1437,144 @@ postgres_insert_deposit_confirmation (void *cls,  /** + * Closure for #deposit_confirmation_cb(). + */ +struct DepositConfirmationContext +{ + +  /** +   * Master public key that is being used. +   */ +  const struct TALER_MasterPublicKeyP *master_pub; + +  /** +   * Function to call for each deposit confirmation. +   */ +  TALER_AUDITORDB_DepositConfirmationCallback cb; + +  /** +   * Closure for @e cb +   */ +  void *cb_cls; + +  /** +   * Query status to return. +   */ +  enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Helper function for #postgres_get_deposit_confirmations(). + * To be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct DepositConfirmationContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +deposit_confirmation_cb (void *cls, +                         PGresult *result, +                         unsigned int num_results) +{ +  struct DepositConfirmationContext *dcc = cls; + +  for (unsigned int i = 0; i < num_results; i++) +  { +    uint64_t serial_id; +    struct TALER_AUDITORDB_DepositConfirmation dc = { +      .master_public_key = *dcc->master_pub +    }; +    struct GNUNET_PQ_ResultSpec rs[] = { +      GNUNET_PQ_result_spec_uint64 ("serial_id", +                                    &serial_id), +      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", +                                            &dc.h_contract_terms), +      GNUNET_PQ_result_spec_auto_from_type ("h_wire", +                                            &dc.h_wire), +      GNUNET_PQ_result_spec_absolute_time ("timetamp", +                                           &dc.timestamp), +      GNUNET_PQ_result_spec_absolute_time ("refund_deadline", +                                           &dc.refund_deadline), +      TALER_PQ_result_spec_amount ("amount_without_fee", +                                   &dc.amount_without_fee), +      GNUNET_PQ_result_spec_auto_from_type ("coin_pub", +                                            &dc.coin_pub), +      GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", +                                            &dc.merchant), +      GNUNET_PQ_result_spec_auto_from_type ("exchange_sig", +                                            &dc.exchange_sig), +      GNUNET_PQ_result_spec_auto_from_type ("exchange_pub", +                                            &dc.exchange_pub), +      GNUNET_PQ_result_spec_auto_from_type ("master_sig", +                                            &dc.master_sig), +      GNUNET_PQ_result_spec_end +    }; + +    if (GNUNET_OK != +        GNUNET_PQ_extract_result (result, +				  rs, +				  i)) +    { +      GNUNET_break (0); +      dcc->qs = GNUNET_DB_STATUS_HARD_ERROR; +      return; +    } +    dcc->qs = i + 1; +    dcc->cb (dcc->cb_cls, +             serial_id, +             &dc); +  } +} + + +/** + * Get information about deposit confirmations from the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param master_pub for which exchange do we want to get deposit confirmations + * @param start_id row/serial ID where to start the iteration (0 from + *                  the start, exclusive, i.e. serial_ids must start from 1) + * @param cb function to call with results + * @param cb_cls closure for @a cb + * @return query result status + */ +static enum GNUNET_DB_QueryStatus +postgres_get_deposit_confirmations (void *cls, +                                    struct TALER_AUDITORDB_Session *session, +                                    const struct TALER_MasterPublicKeyP *master_public_key, +                                    uint64_t start_id, +                                    TALER_AUDITORDB_DepositConfirmationCallback cb, +                                    void *cb_cls) +{ +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_auto_from_type (master_public_key), +    GNUNET_PQ_query_param_uint64 (&start_id), +    GNUNET_PQ_query_param_end +  }; +  struct DepositConfirmationContext dcc = { +    .master_pub = master_public_key, +    .cb = cb, +    .cb_cls = cb_cls +  }; +  enum GNUNET_DB_QueryStatus qs; + +  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, +					     "auditor_deposit_confirmation_select", +					     params, +					     &deposit_confirmation_cb, +					     &dcc); +  if (qs > 0) +    return dcc.qs; +  GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); +  return qs; +} + + + +/**   * Insert information about a denomination key and in particular   * the properties (value, fees, expiration times) the coins signed   * with this key have. @@ -3264,6 +3422,7 @@ libtaler_plugin_auditordb_postgres_init (void *cls)    plugin->list_exchanges = &postgres_list_exchanges;    plugin->insert_exchange_signkey = &postgres_insert_exchange_signkey;    plugin->insert_deposit_confirmation = &postgres_insert_deposit_confirmation; +  plugin->get_deposit_confirmations = &postgres_get_deposit_confirmations;    plugin->select_denomination_info = &postgres_select_denomination_info;    plugin->insert_denomination_info = &postgres_insert_denomination_info; diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 52344d64..3e91218c 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -142,7 +142,8 @@ deposit_transaction (void *cls,    qs = TEH_plugin->have_deposit (TEH_plugin->cls,  				 session, -				 deposit); +				 deposit, +                                 GNUNET_YES /* check refund deadline */);    if (qs < 0)    {      if (GNUNET_DB_STATUS_HARD_ERROR == qs) diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 3cd467c0..2ce865ae 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -2730,6 +2730,7 @@ postgres_get_reserve_history (void *cls,   * @param cls the `struct PostgresClosure` with the plugin-specific state   * @param session database connection   * @param deposit deposit to search for + * @param check_extras wether to check extra fields match or not   * @return 1 if we know this operation,   *         0 if this exact deposit is unknown to us,   *         otherwise transaction error status @@ -2737,7 +2738,8 @@ postgres_get_reserve_history (void *cls,  static enum GNUNET_DB_QueryStatus  postgres_have_deposit (void *cls,                         struct TALER_EXCHANGEDB_Session *session, -                       const struct TALER_EXCHANGEDB_Deposit *deposit) +                       const struct TALER_EXCHANGEDB_Deposit *deposit, +                       int check_extras)  {    struct GNUNET_PQ_QueryParam params[] = {      GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub), @@ -2755,8 +2757,6 @@ postgres_have_deposit (void *cls,  					 &deposit2.refund_deadline),      TALER_PQ_result_spec_absolute_time ("wire_deadline",  					 &deposit2.wire_deadline), -    GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", -					  &deposit2.h_contract_terms),      GNUNET_PQ_result_spec_auto_from_type ("h_wire",  					  &deposit2.h_wire),      GNUNET_PQ_result_spec_end @@ -2774,18 +2774,16 @@ postgres_have_deposit (void *cls,      return qs;    /* Now we check that the other information in @a deposit       also matches, and if not report inconsistencies. */ -  if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee, -			       &deposit2.amount_with_fee)) || -       (deposit->timestamp.abs_value_us != -	deposit2.timestamp.abs_value_us) || +  if ( ( (check_extras) && +         ( (0 != TALER_amount_cmp (&deposit->amount_with_fee, +                                   &deposit2.amount_with_fee)) || +           (deposit->timestamp.abs_value_us != +            deposit2.timestamp.abs_value_us) ) ) ||         (deposit->refund_deadline.abs_value_us != -	deposit2.refund_deadline.abs_value_us) || -       (0 != memcmp (&deposit->h_contract_terms, -		     &deposit2.h_contract_terms, -		     sizeof (struct GNUNET_HashCode))) || +        deposit2.refund_deadline.abs_value_us) ||         (0 != memcmp (&deposit->h_wire, -		     &deposit2.h_wire, -		     sizeof (struct GNUNET_HashCode))) ) +                     &deposit2.h_wire, +                     sizeof (struct GNUNET_HashCode)) ) )    {      /* Inconsistencies detected! Does not match!  (We might want to         expand the API with a 'get_deposit' function to return the diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h index 76dcc350..92494ab3 100644 --- a/src/include/taler_auditordb_plugin.h +++ b/src/include/taler_auditordb_plugin.h @@ -346,6 +346,20 @@ struct TALER_AUDITORDB_DepositConfirmation  /** + * Function called with deposit confirmations stored in + * the auditor's database. + * + * @param cls closure + * @param serial_id location of the @a dc in the database + * @param dc the deposit confirmation itself + */ +typedef void +(*TALER_AUDITORDB_DepositConfirmationCallback)(void *cls, +                                               uint64_t serial_id, +                                               const struct TALER_AUDITORDB_DepositConfirmation *dc); + + +/**   * Handle for one session with the database.   */  struct TALER_AUDITORDB_Session; @@ -526,6 +540,27 @@ struct TALER_AUDITORDB_Plugin    /** +   * Get information about a deposit confirmations from the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param session connection to the database +   * @param master_pub for which exchange do we want to get deposit confirmations +   * @param start_id row/serial ID where to start the iteration (0 from +   *                  the start, exclusive, i.e. serial_ids must start from 1) +   * @param cb function to call with results +   * @param cb_cls closure for @a cb +   * @return query result status +   */ +  enum GNUNET_DB_QueryStatus +  (*get_deposit_confirmations) (void *cls, +                                struct TALER_AUDITORDB_Session *session, +                                const struct TALER_MasterPublicKeyP *master_public_key, +                                uint64_t start_id, +                                TALER_AUDITORDB_DepositConfirmationCallback cb, +                                void *cb_cls); + + +  /**     * Insert information about a denomination key and in particular     * the properties (value, fees, expiration times) the coins signed     * with this key have. diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index c8417a3a..89df4219 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1431,6 +1431,7 @@ struct TALER_EXCHANGEDB_Plugin     * @param cls the @e cls of this struct with the plugin-specific state     * @param session database connection     * @param deposit deposit to search for +   * @param check_extras wether to check extra fields or not     * @return 1 if we know this operation,     *         0 if this exact deposit is unknown to us,     *         otherwise transaction error status @@ -1438,7 +1439,8 @@ struct TALER_EXCHANGEDB_Plugin    enum GNUNET_DB_QueryStatus    (*have_deposit) (void *cls,                     struct TALER_EXCHANGEDB_Session *session, -                   const struct TALER_EXCHANGEDB_Deposit *deposit); +                   const struct TALER_EXCHANGEDB_Deposit *deposit, +                   int check_extras);    /**  | 
