diff options
Diffstat (limited to 'src/exchange-tools')
| -rw-r--r-- | src/exchange-tools/taler-exchange-offline.c | 617 | 
1 files changed, 616 insertions, 1 deletions
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c index eb64a6c9..660864b7 100644 --- a/src/exchange-tools/taler-exchange-offline.c +++ b/src/exchange-tools/taler-exchange-offline.c @@ -1,6 +1,6 @@  /*     This file is part of TALER -   Copyright (C) 2020, 2021, 2022 Taler Systems SA +   Copyright (C) 2020-2023 Taler Systems SA     TALER is free software; you can redistribute it and/or modify it under the     terms of the GNU General Public License as published by the Free Software @@ -113,6 +113,16 @@  #define OP_DRAIN_PROFITS "exchange-drain-profits-0"  /** + * Setup AML staff. + */ +#define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0" + +/** + * Setup partner exchange for wad transfers. + */ +#define OP_ADD_PARTNER "exchange-add-partner-0" + +/**   * Our private key, initialized in #load_offline_key().   */  static struct TALER_MasterPrivateKeyP master_priv; @@ -499,6 +509,62 @@ struct UploadExtensionsRequest  /** + * Data structure for AML staff requests. + */ +struct AmlStaffRequest +{ + +  /** +   * Kept in a DLL. +   */ +  struct AmlStaffRequest *next; + +  /** +   * Kept in a DLL. +   */ +  struct AmlStaffRequest *prev; + +  /** +   * Operation handle. +   */ +  struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h; + +  /** +   * Array index of the associated command. +   */ +  size_t idx; +}; + + +/** + * Data structure for partner add requests. + */ +struct PartnerAddRequest +{ + +  /** +   * Kept in a DLL. +   */ +  struct PartnerAddRequest *next; + +  /** +   * Kept in a DLL. +   */ +  struct PartnerAddRequest *prev; + +  /** +   * Operation handle. +   */ +  struct TALER_EXCHANGE_ManagementAddPartner *h; + +  /** +   * Array index of the associated command. +   */ +  size_t idx; +}; + + +/**   * Next work item to perform.   */  static struct GNUNET_SCHEDULER_Task *nxt; @@ -508,6 +574,27 @@ static struct GNUNET_SCHEDULER_Task *nxt;   */  static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh; + +/** + * Active AML staff change requests. + */ +static struct AmlStaffRequest *asr_head; + +/** + * Active AML staff change requests. + */ +static struct AmlStaffRequest *asr_tail; + +/** + * Active partner add requests. + */ +static struct PartnerAddRequest *par_head; + +/** + * Active partner add requests. + */ +static struct PartnerAddRequest *par_tail; +  /**   * Active denomiantion revocation requests.   */ @@ -630,6 +717,36 @@ do_shutdown (void *cls)    (void) cls;    { +    struct AmlStaffRequest *asr; + +    while (NULL != (asr = asr_head)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Aborting incomplete AML staff update #%u\n", +                  (unsigned int) asr->idx); +      TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h); +      GNUNET_CONTAINER_DLL_remove (asr_head, +                                   asr_tail, +                                   asr); +      GNUNET_free (asr); +    } +  } +  { +    struct PartnerAddRequest *par; + +    while (NULL != (par = par_head)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Aborting incomplete partner add request #%u\n", +                  (unsigned int) par->idx); +      TALER_EXCHANGE_management_add_partner_cancel (par->h); +      GNUNET_CONTAINER_DLL_remove (par_head, +                                   par_tail, +                                   par); +      GNUNET_free (par); +    } +  } +  {      struct DenomRevocationRequest *drr;      while (NULL != (drr = drr_head)) @@ -842,6 +959,8 @@ static void  test_shutdown (void)  {    if ( (NULL == drr_head) && +       (NULL == par_head) && +       (NULL == asr_head) &&         (NULL == srr_head) &&         (NULL == aar_head) &&         (NULL == adr_head) && @@ -2215,6 +2334,221 @@ upload_extensions (const char *exchange_url,  /** + * Function called with information about the add partner operation. + * + * @param cls closure with a `struct PartnerAddRequest` + * @param hr HTTP response data + */ +static void +add_partner_cb ( +  void *cls, +  const struct TALER_EXCHANGE_HttpResponse *hr) +{ +  struct PartnerAddRequest *par = cls; + +  if (MHD_HTTP_NO_CONTENT != hr->http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Upload failed for command %u with status %u: %s (%s)\n", +                (unsigned int) par->idx, +                hr->http_status, +                TALER_ErrorCode_get_hint (hr->ec), +                hr->hint); +    global_ret = EXIT_FAILURE; +  } +  GNUNET_CONTAINER_DLL_remove (par_head, +                               par_tail, +                               par); +  GNUNET_free (par); +  test_shutdown (); +} + + +/** + * Add partner action. + * + * @param exchange_url base URL of the exchange + * @param idx index of the operation we are performing (for logging) + * @param value arguments for add partner + */ +static void +add_partner (const char *exchange_url, +             size_t idx, +             const json_t *value) +{ +  struct TALER_MasterPublicKeyP partner_pub; +  struct GNUNET_TIME_Timestamp start_date; +  struct GNUNET_TIME_Timestamp end_date; +  struct GNUNET_TIME_Relative wad_frequency; +  struct TALER_Amount wad_fee; +  const char *partner_base_url; +  struct TALER_MasterSignatureP master_sig; +  struct PartnerAddRequest *par; +  struct GNUNET_JSON_Specification spec[] = { +    GNUNET_JSON_spec_fixed_auto ("partner_pub", +                                 &partner_pub), +    TALER_JSON_spec_amount ("wad_fee", +                            currency, +                            &wad_fee), +    GNUNET_JSON_spec_relative_time ("wad_frequency", +                                    &wad_frequency), +    GNUNET_JSON_spec_timestamp ("start_date", +                                &start_date), +    GNUNET_JSON_spec_timestamp ("end_date", +                                &end_date), +    GNUNET_JSON_spec_string ("partner_base_url", +                             &partner_base_url), +    GNUNET_JSON_spec_fixed_auto ("master_sig", +                                 &master_sig), +    GNUNET_JSON_spec_end () +  }; +  const char *err_name; +  unsigned int err_line; + +  if (GNUNET_OK != +      GNUNET_JSON_parse (value, +                         spec, +                         &err_name, +                         &err_line)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Invalid input to add partner: %s#%u at %u (skipping)\n", +                err_name, +                err_line, +                (unsigned int) idx); +    json_dumpf (value, +                stderr, +                JSON_INDENT (2)); +    global_ret = EXIT_FAILURE; +    test_shutdown (); +    return; +  } +  par = GNUNET_new (struct PartnerAddRequest); +  par->idx = idx; +  par->h = +    TALER_EXCHANGE_management_add_partner (ctx, +                                           exchange_url, +                                           &partner_pub, +                                           start_date, +                                           end_date, +                                           wad_frequency, +                                           &wad_fee, +                                           partner_base_url, +                                           &master_sig, +                                           &add_partner_cb, +                                           par); +  GNUNET_CONTAINER_DLL_insert (par_head, +                               par_tail, +                               par); +} + + +/** + * Function called with information about the AML officer update operation. + * + * @param cls closure with a `struct AmlStaffRequest` + * @param hr HTTP response data + */ +static void +update_aml_officer_cb ( +  void *cls, +  const struct TALER_EXCHANGE_HttpResponse *hr) +{ +  struct AmlStaffRequest *asr = cls; + +  if (MHD_HTTP_NO_CONTENT != hr->http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Upload failed for command %u with status %u: %s (%s)\n", +                (unsigned int) asr->idx, +                hr->http_status, +                TALER_ErrorCode_get_hint (hr->ec), +                hr->hint); +    global_ret = EXIT_FAILURE; +  } +  GNUNET_CONTAINER_DLL_remove (asr_head, +                               asr_tail, +                               asr); +  GNUNET_free (asr); +  test_shutdown (); +} + + +/** + * Upload AML staff action. + * + * @param exchange_url base URL of the exchange + * @param idx index of the operation we are performing (for logging) + * @param value arguments for AML staff change + */ +static void +update_aml_staff (const char *exchange_url, +                  size_t idx, +                  const json_t *value) +{ +  struct TALER_AmlOfficerPublicKeyP officer_pub; +  const char *officer_name; +  struct GNUNET_TIME_Timestamp change_date; +  bool is_active; +  bool read_only; +  struct TALER_MasterSignatureP master_sig; +  struct AmlStaffRequest *asr; +  struct GNUNET_JSON_Specification spec[] = { +    GNUNET_JSON_spec_fixed_auto ("officer_pub", +                                 &officer_pub), +    GNUNET_JSON_spec_timestamp ("change_date", +                                &change_date), +    GNUNET_JSON_spec_bool ("is_active", +                           &is_active), +    GNUNET_JSON_spec_bool ("read_only", +                           &read_only), +    GNUNET_JSON_spec_string ("officer_name", +                             &officer_name), +    GNUNET_JSON_spec_fixed_auto ("master_sig", +                                 &master_sig), +    GNUNET_JSON_spec_end () +  }; +  const char *err_name; +  unsigned int err_line; + +  if (GNUNET_OK != +      GNUNET_JSON_parse (value, +                         spec, +                         &err_name, +                         &err_line)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Invalid input to AML staff update: %s#%u at %u (skipping)\n", +                err_name, +                err_line, +                (unsigned int) idx); +    json_dumpf (value, +                stderr, +                JSON_INDENT (2)); +    global_ret = EXIT_FAILURE; +    test_shutdown (); +    return; +  } +  asr = GNUNET_new (struct AmlStaffRequest); +  asr->idx = idx; +  asr->h = +    TALER_EXCHANGE_management_update_aml_officer (ctx, +                                                  exchange_url, +                                                  &officer_pub, +                                                  officer_name, +                                                  change_date, +                                                  is_active, +                                                  read_only, +                                                  &master_sig, +                                                  &update_aml_officer_cb, +                                                  asr); +  GNUNET_CONTAINER_DLL_insert (asr_head, +                               asr_tail, +                               asr); +} + + +/**   * Perform uploads based on the JSON in #out.   *   * @param exchange_url base URL of the exchange to use @@ -2267,6 +2601,14 @@ trigger_upload (const char *exchange_url)        .key = OP_EXTENSIONS,        .cb = &upload_extensions      }, +    { +      .key = OP_UPDATE_AML_STAFF, +      .cb = &update_aml_staff +    }, +    { +      .key = OP_ADD_PARTNER, +      .cb = &add_partner +    },      /* array termination */      {        .key = NULL @@ -3041,6 +3383,261 @@ do_drain (char *const *args)  /** + * Add partner. + * + * @param args the array of command-line arguments to process next; + *        args[0] must be the partner's master public key, args[1] the partner's + *        API base URL, args[2] the wad fee, args[3] the wad frequency, and + *        args[4] the year (including possibly 'now') + */ +static void +do_add_partner (char *const *args) +{ +  struct TALER_MasterPublicKeyP partner_pub; +  struct GNUNET_TIME_Timestamp start_date; +  struct GNUNET_TIME_Timestamp end_date; +  struct GNUNET_TIME_Relative wad_frequency; +  struct TALER_Amount wad_fee; +  const char *partner_base_url; +  struct TALER_MasterSignatureP master_sig; +  char dummy; +  unsigned int year; + +  if (NULL != in) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Downloaded data was not consumed, not adding partner\n"); +    test_shutdown (); +    global_ret = EXIT_FAILURE; +    return; +  } +  if ( (NULL == args[0]) || +       (GNUNET_OK != +        GNUNET_STRINGS_string_to_data (args[0], +                                       strlen (args[0]), +                                       &partner_pub, +                                       sizeof (partner_pub))) ) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "You must specify the partner master public key as first argument for this subcommand\n"); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  if ( (NULL == args[1]) || +       (0 != strncmp ("http", +                      args[1], +                      strlen ("http"))) ) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  partner_base_url = args[1]; +  if ( (NULL == args[2]) || +       (GNUNET_OK != +        TALER_string_to_amount (args[2], +                                &wad_fee)) ) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Invalid amount `%s' specified for wad fee of partner\n", +                args[2]); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  if ( (NULL == args[3]) || +       (GNUNET_OK != +        GNUNET_STRINGS_fancy_time_to_relative (args[3], +                                               &wad_frequency)) ) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Invalid wad frequency `%s' specified for add partner\n", +                args[3]); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  if ( (NULL == args[4]) || +       ( (1 != sscanf (args[4], +                       "%u%c", +                       &year, +                       &dummy)) && +         (0 != strcasecmp ("now", +                           args[4])) ) ) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Invalid year `%s' specified for add partner\n", +                args[4]); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  if (0 == strcasecmp ("now", +                       args[4])) +    year = GNUNET_TIME_get_current_year (); +  start_date = GNUNET_TIME_absolute_to_timestamp ( +    GNUNET_TIME_year_to_time (year)); +  end_date = GNUNET_TIME_absolute_to_timestamp ( +    GNUNET_TIME_year_to_time (year + 1)); + +  if (GNUNET_OK != +      load_offline_key (GNUNET_NO)) +    return; +  TALER_exchange_offline_partner_details_sign (&partner_pub, +                                               start_date, +                                               end_date, +                                               wad_frequency, +                                               &wad_fee, +                                               partner_base_url, +                                               &master_priv, +                                               &master_sig); +  output_operation (OP_ADD_PARTNER, +                    GNUNET_JSON_PACK ( +                      GNUNET_JSON_pack_string ("partner_base_url", +                                               partner_base_url), +                      GNUNET_JSON_pack_time_rel ("wad_frequency", +                                                 wad_frequency), +                      GNUNET_JSON_pack_timestamp ("start_date", +                                                  start_date), +                      GNUNET_JSON_pack_timestamp ("end_date", +                                                  end_date), +                      GNUNET_JSON_pack_data_auto ("partner_pub", +                                                  &partner_pub), +                      GNUNET_JSON_pack_data_auto ("master_sig", +                                                  &master_sig))); +  next (args + 5); +} + + +/** + * Enable or disable AML staff. + * + * @param is_active true to enable, false to disable + * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only) + */ +static void +do_set_aml_staff (bool is_active, +                  char *const *args) +{ +  struct TALER_AmlOfficerPublicKeyP officer_pub; +  const char *officer_name; +  bool read_only; +  struct TALER_MasterSignatureP master_sig; +  struct GNUNET_TIME_Timestamp now +    = GNUNET_TIME_timestamp_get (); + +  if (NULL != in) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Downloaded data was not consumed, not updating AML staff status\n"); +    test_shutdown (); +    global_ret = EXIT_FAILURE; +    return; +  } +  if ( (NULL == args[0]) || +       (GNUNET_OK != +        GNUNET_STRINGS_string_to_data (args[0], +                                       strlen (args[0]), +                                       &officer_pub, +                                       sizeof (officer_pub))) ) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "You must specify the AML officer's public key as first argument for this subcommand\n"); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  if (NULL == args[1]) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); +    test_shutdown (); +    global_ret = EXIT_INVALIDARGUMENT; +    return; +  } +  officer_name = args[1]; +  if (is_active) +  { +    if ( (NULL == args[2]) || +         ( (0 != strcmp (args[2], +                         "ro")) && +           (0 != strcmp (args[2], +                         "rw")) ) ) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", +                  args[2]); +      test_shutdown (); +      global_ret = EXIT_INVALIDARGUMENT; +      return; +    } +    read_only = (0 == strcmp (args[2], +                              "ro")); +  } +  else +  { +    read_only = true; +  } +  if (GNUNET_OK != +      load_offline_key (GNUNET_NO)) +    return; +  TALER_exchange_offline_aml_officer_status_sign (&officer_pub, +                                                  officer_name, +                                                  now, +                                                  is_active, +                                                  read_only, +                                                  &master_priv, +                                                  &master_sig); +  output_operation (OP_UPDATE_AML_STAFF, +                    GNUNET_JSON_PACK ( +                      GNUNET_JSON_pack_string ("officer_name", +                                               officer_name), +                      GNUNET_JSON_pack_timestamp ("change_date", +                                                  now), +                      GNUNET_JSON_pack_bool ("is_active", +                                             is_active), +                      GNUNET_JSON_pack_bool ("read_only", +                                             read_only), +                      GNUNET_JSON_pack_data_auto ("officer_pub", +                                                  &officer_pub), +                      GNUNET_JSON_pack_data_auto ("master_sig", +                                                  &master_sig))); +  next (args + (is_active ? 3 : 2)); +} + + +/** + * Disable AML staff. + * + * @param args the array of command-line arguments to process next; + *        args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) + */ +static void +disable_aml_staff (char *const *args) +{ +  do_set_aml_staff (false, +                    args); +} + + +/** + * Enable AML staff. + * + * @param args the array of command-line arguments to process next; + *        args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) + */ +static void +enable_aml_staff (char *const *args) +{ +  do_set_aml_staff (true, +                    args); +} + + +/**   * Function called with information about future keys.  Dumps the JSON output   * (on success), either into an internal buffer or to stdout (depending on   * whether there are subsequent commands). @@ -4476,6 +5073,24 @@ work (void *cls)        .cb = &do_drain      },      { +      .name = "add-partner", +      .help = +        "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)", +      .cb = &do_add_partner +    }, +    { +      .name = "aml-enable", +      .help = +        "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", +      .cb = &enable_aml_staff +    }, +    { +      .name = "aml-disable", +      .help = +        "disable AML staff member (staff member public key and legal name must be given as arguments)", +      .cb = &disable_aml_staff +    }, +    {        .name = "upload",        .help =          "upload operation result to exchange (to be performed online!)",  | 
