diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/auditor/taler-helper-auditor-reserves.c | 5 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_withdraw.c | 113 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 122 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 33 | 
4 files changed, 265 insertions, 8 deletions
diff --git a/src/auditor/taler-helper-auditor-reserves.c b/src/auditor/taler-helper-auditor-reserves.c index aa9c241b..c27574d1 100644 --- a/src/auditor/taler-helper-auditor-reserves.c +++ b/src/auditor/taler-helper-auditor-reserves.c @@ -1083,10 +1083,13 @@ verify_reserve_balance (void *cls,         internal audit, as otherwise the balance of the 'reserves' table         is not replicated at the auditor. */      struct TALER_EXCHANGEDB_Reserve reserve; +    struct TALER_EXCHANGEDB_KycStatus kyc;      reserve.pub = rs->reserve_pub;      qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls, -                                      &reserve); +                                      &reserve, +                                      &kyc); +    // FIXME: figure out what to do with KYC status!      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)      {        /* If the exchange doesn't have this reserve in the summary, it diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index ca5618af..4839ec97 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -129,10 +129,45 @@ struct WithdrawContext     */    struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; +  /** +   * KYC status for the operation. +   */ +  struct TALER_EXCHANGEDB_KycStatus kyc; + +  /** +   * Set to true if the operation was denied due to +   * failing @e kyc checks. +   */ +  bool kyc_denied; +  };  /** + * Function called with another amount that was + * already withdrawn. Accumulates all amounts in + * @a cls. + * + * @param[in,out] cls a `struct TALER_Amount` + * @param val value to add to @a cls + */ +static void +accumulate_withdraws (void *cls, +                      const struct TALER_Amount *val) +{ +  struct TALER_Amount *acc = cls; + +  if (GNUNET_OK != +      TALER_amount_is_valid (acc)) +    return; /* ignore */ +  GNUNET_break (0 <= +                TALER_amount_add (acc, +                                  acc, +                                  val)); +} + + +/**   * Function implementing withdraw transaction.  Runs the   * transaction logic; IF it returns a non-error code, the transaction   * logic MUST NOT queue a MHD response.  IF it returns an hard error, @@ -165,7 +200,6 @@ withdraw_transaction (void *cls,    struct TALER_EXCHANGEDB_Reserve r;    enum GNUNET_DB_QueryStatus qs;    struct TALER_DenominationSignature denom_sig; -  struct TALER_EXCHANGEDB_KycStatus kyc;  #if OPTIMISTIC_SIGN    /* store away optimistic signature to protect @@ -211,7 +245,7 @@ withdraw_transaction (void *cls,                TALER_B2S (&r.pub));    qs = TEH_plugin->reserves_get (TEH_plugin->cls,                                   &r, -                                 &kyc); +                                 &wc->kyc);    if (0 > qs)    {      if (GNUNET_DB_STATUS_HARD_ERROR == qs) @@ -270,11 +304,60 @@ withdraw_transaction (void *cls,      return GNUNET_DB_STATUS_HARD_ERROR;    } -  if ( (! kyc.ok) && -       (TEH_KYC_NONE != TEH_kyc_config.mode) ) +  if ( (! wc->kyc.ok) && +       (TEH_KYC_NONE != TEH_kyc_config.mode) && +       (TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) ) +  { +    /* Wallet-to-wallet payments _always_ require KYC */ +    wc->kyc_denied = true; +    return qs; +  } +  if ( (! wc->kyc.ok) && +       (TEH_KYC_NONE != TEH_kyc_config.mode) && +       (TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) && +       (! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )    { -    // FIXME: check if we are above the limit -    // for KYC, and if so, deny the transaction! +    /* Withdraws require KYC if above threshold */ +    struct TALER_Amount acc; +    enum GNUNET_DB_QueryStatus qs2; + +    TALER_amount_set_zero (TEH_currency, +                           &acc); +    accumulate_withdraws (&acc, +                          &wc->amount_required); +    qs2 = TEH_plugin->select_withdraw_amounts_by_account ( +      TEH_plugin->cls, +      &wc->wsrd.reserve_pub, +      TEH_kyc_config.withdraw_period, +      &accumulate_withdraws, +      &acc); +    if (0 > qs2) +    { +      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2); +      if (GNUNET_DB_STATUS_HARD_ERROR == qs2) +        *mhd_ret = TALER_MHD_reply_with_error (connection, +                                               MHD_HTTP_INTERNAL_SERVER_ERROR, +                                               TALER_EC_GENERIC_DB_FETCH_FAILED, +                                               "withdraw details"); +      return qs2; +    } + +    if (GNUNET_OK != +        TALER_amount_is_valid (&acc)) +    { +      GNUNET_break (0); +      *mhd_ret = TALER_MHD_reply_with_ec (connection, +                                          TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, +                                          NULL); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +    if (1 == /* 1: acc > withdraw_limit */ +        TALER_amount_cmp (&acc, +                          &TEH_kyc_config.withdraw_limit)) +    { +      wc->kyc_denied = true; +      return qs; +    }    }    /* Balance is good, sign the coin! */ @@ -338,6 +421,9 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,    enum TALER_ErrorCode ec;    struct TEH_DenominationKey *dk; +  memset (&wc, +          0, +          sizeof (wc));    if (GNUNET_OK !=        GNUNET_STRINGS_string_to_data (args[0],                                       strlen (args[0]), @@ -480,6 +566,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,  #endif    /* run transaction and sign (if not optimistically signed before) */ +  wc.kyc_denied = false;    {      MHD_RESULT mhd_ret; @@ -499,9 +586,21 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,      }    } -  /* Clean up and send back final (positive) response */ +  /* Clean up and send back final response */    GNUNET_JSON_parse_free (spec); +  if (wc.kyc_denied) +  { +    if (NULL != wc.collectable.sig.rsa_signature) +      GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature); + +    return TALER_MHD_REPLY_JSON_PACK ( +      rc->connection, +      MHD_HTTP_ACCEPTED, +      GNUNET_JSON_pack_uint64 ("payment_target_uuid", +                               wc.kyc.payment_target_uuid)); +  } +    {      MHD_RESULT ret; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 34b785e7..eda6468b 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -4389,6 +4389,126 @@ postgres_get_reserve_history (void *cls,  /** + * Closure for withdraw_amount_by_account_cb() + */ +struct WithdrawAmountByAccountContext +{ +  /** +   * Function to call on each amount. +   */ +  TALER_EXCHANGEDB_WithdrawHistoryCallback cb; + +  /** +   * Closure for @e cb +   */ +  void *cb_cls; + +  /** +   * Our plugin's context. +   */ +  struct PostgresClosure *pg; + +  /** +   * Set to true on failures. +   */ +  bool failed; +}; + + +/** + * Helper function for #postgres_select_withdraw_amounts_by_account(). + * To be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct WithdrawAmountByAccountContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +withdraw_amount_by_account_cb (void *cls, +                               PGresult *result, +                               unsigned int num_results) +{ +  struct WithdrawAmountByAccountContext *wac = cls; +  struct PostgresClosure *pg = wac->pg; + +  for (unsigned int i = 0; num_results; i++) +  { +    struct TALER_Amount val; +    struct GNUNET_PQ_ResultSpec rs[] = { +      TALER_PQ_RESULT_SPEC_AMOUNT ("val", +                                   &val), +      GNUNET_PQ_result_spec_end +    }; + +    if (GNUNET_OK != +        GNUNET_PQ_extract_result (result, +                                  rs, +                                  i)) +    { +      GNUNET_break (0); +      wac->failed = true; +      return; +    } +    wac->cb (wac->cb_cls, +             &val); +  } +} + + +/** + * Find out all of the amounts that have been withdrawn + * so far from the same bank account that created the + * given reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param reserve_pub reserve to select withdrawals by + * @param duration how far back should we select withdrawals + * @param cb function to call on each amount withdrawn + * @param cb_cls closure for @a cb + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +postgres_select_withdraw_amounts_by_account ( +  void *cls, +  const struct TALER_ReservePublicKeyP *reserve_pub, +  struct GNUNET_TIME_Relative duration, +  TALER_EXCHANGEDB_WithdrawHistoryCallback cb, +  void *cb_cls) +{ +  struct PostgresClosure *pg = cls; +  struct WithdrawAmountByAccountContext wac = { +    .pg = pg, +    .cb = cb, +    .cb_cls = cb_cls +  }; +  struct GNUNET_TIME_Absolute start +    = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (), +                                     duration); +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_auto_from_type (reserve_pub), +    GNUNET_PQ_query_param_absolute_time (&start), +    GNUNET_PQ_query_param_end +  }; +  enum GNUNET_DB_QueryStatus qs; + +  qs = GNUNET_PQ_eval_prepared_multi_select ( +    pg->conn, +    "select_XXX", +    params, +    &withdraw_amount_by_account_cb, +    &wac); + +  if (wac.failed) +  { +    GNUNET_break (0); +    qs = GNUNET_DB_STATUS_HARD_ERROR; +  } +  return qs; +} + + +/**   * Check if we have the specified deposit already in the database.   *   * @param cls the `struct PostgresClosure` with the plugin-specific state @@ -10957,6 +11077,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)    plugin->get_withdraw_info = &postgres_get_withdraw_info;    plugin->insert_withdraw_info = &postgres_insert_withdraw_info;    plugin->get_reserve_history = &postgres_get_reserve_history; +  plugin->select_withdraw_amounts_by_account +    = &postgres_select_withdraw_amounts_by_account;    plugin->free_reserve_history = &common_free_reserve_history;    plugin->count_known_coins = &postgres_count_known_coins;    plugin->ensure_coin_known = &postgres_ensure_coin_known; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index d94a985d..34196aad 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -2012,6 +2012,18 @@ typedef enum GNUNET_GenericReturnValue  /** + * Function called with the amounts historically + * withdrawn from the same origin account. + * + * @param cls closure + * @param val one of the withdrawn amounts + */ +typedef void +(*TALER_EXCHANGEDB_WithdrawHistoryCallback)( +  void *cls, +  const struct TALER_Amount *val); + +/**   * Function called with details about expired reserves.   *   * @param cls closure @@ -2451,6 +2463,27 @@ struct TALER_EXCHANGEDB_Plugin    /** +   * Find out all of the amounts that have been withdrawn +   * so far from the same bank account that created the +   * given reserve. +   * +   * @param cls closure +   * @param reserve_pub reserve to select withdrawals by +   * @param duration how far back should we select withdrawals +   * @param cb function to call on each amount withdrawn +   * @param cb_cls closure for @a cb +   * @return transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*select_withdraw_amounts_by_account)( +    void *cls, +    const struct TALER_ReservePublicKeyP *reserve_pub, +    struct GNUNET_TIME_Relative duration, +    TALER_EXCHANGEDB_WithdrawHistoryCallback cb, +    void *cb_cls); + + +  /**     * Free memory associated with the given reserve history.     *     * @param cls the @e cls of this struct with the plugin-specific state  | 
