LCOV - code coverage report
Current view: top level - exchangedb - account_history.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.1 % 87 61
Test Date: 2026-09-11 18:55:36 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023, 2024 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Affero 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 Affero General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Affero General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file account_history.c
      18              :  * @brief helper function to build AML inputs from account histories
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "exchangedb_lib.h"
      22              : #include "exchangedb_lib.h"
      23              : #include "taler/taler_kyclogic_lib.h"
      24              : #include "taler/taler_json_lib.h"
      25              : #include "exchange-database/iterate_aml_history.h"
      26              : #include "exchange-database/iterate_kyc_history.h"
      27              : #include "exchange-database/get_kyc_rules.h"
      28              : #include "exchange-database/iterate_aml_attributes.h"
      29              : #include "exchange-database/account_history.h"
      30              : #include <gnunet/gnunet_common.h>
      31              : 
      32              : /**
      33              :  * Function called to expand AML history for the account.
      34              :  *
      35              :  * @param cls a `json_t *` array to build
      36              :  * @param outcome_serial_id row ID of the decision
      37              :  * @param decision_time when was the decision taken
      38              :  * @param justification what was the given justification
      39              :  * @param decider_pub which key signed the decision
      40              :  * @param jproperties what are the new account properties
      41              :  * @param jnew_rules what are the new account rules
      42              :  * @param to_investigate should AML staff investigate
      43              :  *          after the decision
      44              :  * @param is_active is this the active decision
      45              :  */
      46              : static void
      47            2 : add_aml_history_entry (
      48              :   void *cls,
      49              :   uint64_t outcome_serial_id,
      50              :   struct GNUNET_TIME_Timestamp decision_time,
      51              :   const char *justification,
      52              :   const struct TALER_AmlOfficerPublicKeyP *decider_pub,
      53              :   const json_t *jproperties,
      54              :   const json_t *jnew_rules,
      55              :   bool to_investigate,
      56              :   bool is_active)
      57              : {
      58            2 :   json_t *aml_history = cls;
      59              :   json_t *e;
      60              : 
      61            2 :   e = GNUNET_JSON_PACK (
      62              :     GNUNET_JSON_pack_timestamp ("decision_time",
      63              :                                 decision_time),
      64              :     GNUNET_JSON_pack_string ("justification",
      65              :                              justification),
      66              :     GNUNET_JSON_pack_data_auto ("decider_pub",
      67              :                                 decider_pub),
      68              :     /* the column is nullable: a decision may set no properties */
      69              :     GNUNET_JSON_pack_allow_null (
      70              :       GNUNET_JSON_pack_object_incref ("properties",
      71              :                                       (json_t *) jproperties)),
      72              :     GNUNET_JSON_pack_object_incref ("new_rules",
      73              :                                     (json_t *) jnew_rules),
      74              :     GNUNET_JSON_pack_bool ("to_investigate",
      75              :                            to_investigate),
      76              :     GNUNET_JSON_pack_bool ("is_active",
      77              :                            is_active)
      78              :     );
      79            2 :   GNUNET_assert (0 ==
      80              :                  json_array_append_new (aml_history,
      81              :                                         e));
      82            2 : }
      83              : 
      84              : 
      85              : json_t *
      86            1 : TALER_EXCHANGEDB_aml_history_builder (void *cls)
      87              : {
      88            1 :   struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls;
      89            1 :   const struct TALER_NormalizedPaytoHashP *acc = hbc->account;
      90              :   enum GNUNET_DB_QueryStatus qs;
      91              :   json_t *aml_history;
      92              : 
      93            1 :   aml_history = json_array ();
      94            1 :   GNUNET_assert (NULL != aml_history);
      95            1 :   qs = TALER_EXCHANGEDB_iterate_aml_history (
      96              :     hbc->pg,
      97              :     acc,
      98              :     INT64_MAX, /* offset; note: the offset is passed to Postgres as a
      99              :                   signed INT8, so UINT64_MAX would arrive as -1 and
     100              :                   match nothing */
     101              :     -16 * 1024,  /* limit: none for all practical purposes (for now) */
     102              :     &add_aml_history_entry,
     103              :     aml_history);
     104            1 :   switch (qs)
     105              :   {
     106            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     107              :   case GNUNET_DB_STATUS_SOFT_ERROR:
     108            0 :     GNUNET_break (0);
     109            0 :     json_decref (aml_history);
     110            0 :     return NULL;
     111            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     112              :     /* empty history is fine! */
     113            0 :     break;
     114            0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     115            0 :     break;
     116              :   }
     117            1 :   return aml_history;
     118              : }
     119              : 
     120              : 
     121              : /**
     122              :  * Closure for #add_kyc_history_entry.
     123              :  */
     124              : struct KycContext
     125              : {
     126              :   /**
     127              :    * JSON array we are building.
     128              :    */
     129              :   json_t *kyc_history;
     130              : 
     131              :   /**
     132              :    * Key to use to decrypt KYC attributes.
     133              :    */
     134              :   const struct TALER_AttributeEncryptionKeyP *attribute_key;
     135              : };
     136              : 
     137              : 
     138              : /**
     139              :  * Function called to expand KYC history for the account.
     140              :  *
     141              :  * @param cls a `json_t *` array to build
     142              :  * @param provider_name name of the KYC provider
     143              :  *    or NULL for none
     144              :  * @param finished did the KYC process finish
     145              :  * @param error_code error code from the KYC process
     146              :  * @param error_message error message from the KYC process,
     147              :  *    or NULL for none
     148              :  * @param provider_user_id user ID at the provider
     149              :  *    or NULL for none
     150              :  * @param provider_legitimization_id legitimization process ID at the provider
     151              :  *    or NULL for none
     152              :  * @param collection_time when was the data collected
     153              :  * @param expiration_time when does the collected data expire
     154              :  * @param encrypted_attributes_len number of bytes in @a encrypted_attributes
     155              :  * @param encrypted_attributes encrypted KYC attributes
     156              :  */
     157              : static void
     158            2 : add_kyc_history_entry (
     159              :   void *cls,
     160              :   const char *provider_name,
     161              :   bool finished,
     162              :   enum TALER_ErrorCode error_code,
     163              :   const char *error_message,
     164              :   const char *provider_user_id,
     165              :   const char *provider_legitimization_id,
     166              :   struct GNUNET_TIME_Timestamp collection_time,
     167              :   struct GNUNET_TIME_Absolute expiration_time,
     168              :   size_t encrypted_attributes_len,
     169              :   const void *encrypted_attributes)
     170              : {
     171            2 :   struct KycContext *kc = cls;
     172            2 :   json_t *kyc_history = kc->kyc_history;
     173              :   json_t *attributes;
     174              :   json_t *e;
     175              : 
     176            2 :   attributes = TALER_CRYPTO_kyc_attributes_decrypt (
     177              :     kc->attribute_key,
     178              :     encrypted_attributes,
     179              :     encrypted_attributes_len);
     180            2 :   e = GNUNET_JSON_PACK (
     181              :     GNUNET_JSON_pack_string (
     182              :       "provider_name",
     183              :       provider_name),
     184              :     GNUNET_JSON_pack_bool (
     185              :       "finished",
     186              :       finished),
     187              :     TALER_JSON_pack_ec (error_code),
     188              :     GNUNET_JSON_pack_allow_null (
     189              :       GNUNET_JSON_pack_string (
     190              :         "error_message",
     191              :         error_message)),
     192              :     GNUNET_JSON_pack_allow_null (
     193              :       GNUNET_JSON_pack_string (
     194              :         "provider_user_id",
     195              :         provider_user_id)),
     196              :     GNUNET_JSON_pack_allow_null (
     197              :       GNUNET_JSON_pack_string (
     198              :         "provider_legitimization_id",
     199              :         provider_legitimization_id)),
     200              :     GNUNET_JSON_pack_allow_null (
     201              :       GNUNET_JSON_pack_timestamp (
     202              :         "collection_time",
     203              :         collection_time)),
     204              :     GNUNET_JSON_pack_allow_null (
     205              :       GNUNET_JSON_pack_timestamp (
     206              :         "expiration_time",
     207              :         GNUNET_TIME_absolute_to_timestamp (
     208              :           expiration_time))),
     209              :     GNUNET_JSON_pack_allow_null (
     210              :       GNUNET_JSON_pack_object_steal (
     211              :         "attributes",
     212              :         attributes))
     213              :     );
     214              : 
     215            2 :   GNUNET_assert (0 ==
     216              :                  json_array_append_new (kyc_history,
     217              :                                         e));
     218            2 : }
     219              : 
     220              : 
     221              : json_t *
     222            2 : TALER_EXCHANGEDB_kyc_history_builder (void *cls)
     223              : {
     224            2 :   struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls;
     225            2 :   const struct TALER_NormalizedPaytoHashP *acc = hbc->account;
     226              :   enum GNUNET_DB_QueryStatus qs;
     227            2 :   struct KycContext kc = {
     228            2 :     .kyc_history = json_array (),
     229            2 :     .attribute_key = hbc->attribute_key
     230              :   };
     231              : 
     232            2 :   GNUNET_assert (NULL != kc.kyc_history);
     233            2 :   qs = TALER_EXCHANGEDB_iterate_kyc_history (
     234              :     hbc->pg,
     235              :     acc,
     236              :     &add_kyc_history_entry,
     237              :     &kc);
     238            2 :   switch (qs)
     239              :   {
     240            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     241              :   case GNUNET_DB_STATUS_SOFT_ERROR:
     242            0 :     GNUNET_break (0);
     243            0 :     json_decref (kc.kyc_history);
     244            0 :     return NULL;
     245            1 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     246              :     /* empty history is fine! */
     247            1 :     break;
     248            0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     249            0 :     break;
     250              :   }
     251            2 :   return kc.kyc_history;
     252              : }
     253              : 
     254              : 
     255              : json_t *
     256            1 : TALER_EXCHANGEDB_current_rule_builder (void *cls)
     257              : {
     258            1 :   struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls;
     259            1 :   const struct TALER_NormalizedPaytoHashP *acc = hbc->account;
     260              :   enum GNUNET_DB_QueryStatus qs;
     261              :   json_t *jlrs;
     262              : 
     263            1 :   qs = TALER_EXCHANGEDB_get_kyc_rules (
     264              :     hbc->pg,
     265              :     acc,
     266              :     &jlrs);
     267            1 :   switch (qs)
     268              :   {
     269            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     270              :   case GNUNET_DB_STATUS_SOFT_ERROR:
     271            0 :     GNUNET_break (0);
     272            0 :     return NULL;
     273            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     274            0 :     jlrs = TALER_KYCLOGIC_get_default_legi_rules (
     275            0 :       hbc->is_wallet);
     276            0 :     break;
     277            1 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     278            1 :     if (NULL == jlrs)
     279              :     {
     280              :       /* A NULL rule set means the account is on the exchange's default
     281              :          rules (see exchange_do_insert_successor_measure), not that it has
     282              :          no rules at all.  Without this AML programs would be run without
     283              :          "current_rules" and fail. */
     284            0 :       jlrs = TALER_KYCLOGIC_get_default_legi_rules (
     285            0 :         hbc->is_wallet);
     286              :     }
     287            1 :     break;
     288              :   }
     289            1 :   return jlrs;
     290              : }
     291              : 
     292              : 
     293              : /**
     294              :  * Closure for decrypt_attributes().
     295              :  */
     296              : struct DecryptContext
     297              : {
     298              :   /**
     299              :    * Overall context.
     300              :    */
     301              :   const struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc;
     302              : 
     303              :   /**
     304              :    * Where to return the attributes.
     305              :    */
     306              :   json_t *attr;
     307              : };
     308              : 
     309              : 
     310              : /**
     311              :  * Decrypt and return AML attribute information.
     312              :  *
     313              :  * @param cls a `struct DecryptContext *`
     314              :  * @param row_id current row in kyc_attributes table
     315              :  * @param collection_time when were the attributes collected
     316              :  * @param by_aml_officer true if filed by AML officer
     317              :  * @param officer_name name of the officer, NULL if not @a by_aml_officer
     318              :  * @param enc_attributes_size size of @a enc_attributes
     319              :  * @param enc_attributes the encrypted collected attributes
     320              :  */
     321              : static void
     322           11 : decrypt_attributes (
     323              :   void *cls,
     324              :   uint64_t row_id,
     325              :   struct GNUNET_TIME_Timestamp collection_time,
     326              :   bool by_aml_officer,
     327              :   const char *officer_name,
     328              :   size_t enc_attributes_size,
     329              :   const void *enc_attributes)
     330              : {
     331           11 :   struct DecryptContext *decon = cls;
     332              : 
     333              :   (void) row_id;
     334              :   (void) collection_time;
     335              :   (void) officer_name;
     336              :   decon->attr
     337           11 :     = TALER_CRYPTO_kyc_attributes_decrypt (decon->hbc->attribute_key,
     338              :                                            enc_attributes,
     339              :                                            enc_attributes_size);
     340           11 :   GNUNET_break (NULL != decon->attr);
     341           11 : }
     342              : 
     343              : 
     344              : json_t *
     345           12 : TALER_EXCHANGEDB_current_attributes_builder (void *cls)
     346              : {
     347           12 :   struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls;
     348           12 :   const struct TALER_NormalizedPaytoHashP *acc = hbc->account;
     349              :   enum GNUNET_DB_QueryStatus qs;
     350           12 :   struct DecryptContext decon = {
     351              :     .hbc = hbc
     352              :   };
     353              : 
     354           12 :   qs = TALER_EXCHANGEDB_iterate_aml_attributes (
     355              :     hbc->pg,
     356              :     acc,
     357              :     INT64_MAX,
     358              :     -1, /* we only fetch the latest ones */
     359              :     &decrypt_attributes,
     360              :     &decon);
     361           12 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     362              :               "select_aml_attributes returned %d\n",
     363              :               (int) qs);
     364           12 :   switch (qs)
     365              :   {
     366            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     367              :   case GNUNET_DB_STATUS_SOFT_ERROR:
     368            0 :     GNUNET_break (0);
     369            0 :     return NULL;
     370            1 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     371            1 :     decon.attr = json_object ();
     372            1 :     GNUNET_break (NULL != decon.attr);
     373            1 :     break;
     374           11 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     375           11 :     GNUNET_break (NULL != decon.attr);
     376           11 :     break;
     377              :   }
     378           12 :   return decon.attr;
     379              : }
        

Generated by: LCOV version 2.0-1