/*
  This file is part of Anastasis
  Copyright (C) 2020, 2021, 2022 Anastasis SARL

  Anastasis 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
  Foundation; either version 3, or (at your option) any later version.

  Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along with
  Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
*/
/**
 * @file reducer/anastasis_api_redux_state.h
 * @brief typed representation of the reducer state
 * @author Christian Grothoff
 *
 * The reducer's public API (`anastasis_redux.h`) speaks JSON, but the reducer
 * logic itself does not: every entry point parses the incoming JSON into a
 * `struct ANASTASIS_ReduxState` exactly once, operates on that, and serializes
 * it back exactly once on the way out.  The three generic routines at the
 * bottom of this file are the only places that know the JSON encoding.
 *
 * Parsing is strict: a field that is not described here is an error
 * (#TALER_EC_ANASTASIS_REDUCER_STATE_INVALID).  There are two deliberate
 * exceptions, both marked `json_t *` below, where the exact bytes the user
 * supplied are cryptographically load-bearing and must not be normalized:
 * @e identity_attributes (PoW-hashed to derive the account key) and
 * @e core_secret (its serialization *is* the uploaded secret).
 */
#ifndef ANASTASIS_API_REDUX_STATE_H
#define ANASTASIS_API_REDUX_STATE_H

#include "anastasis_redux.h"
#include "anastasis_api_redux.h"


/* ************************ Wrapped primitives *********************** */

/**
 * URL of an Anastasis provider.  Wrapped so that it cannot be confused
 * with the many other strings floating around the reducer (business
 * names, payto URIs, display hints, ...).
 */
struct ANASTASIS_ProviderUrl
{
  /**
   * The URL itself.  Owned by the containing structure.
   */
  char *url;
};


/**
 * Index into the @e policies array of a backup state.
 */
struct ANASTASIS_PolicyIndex
{
  /**
   * The index.
   */
  unsigned int idx;
};


/**
 * Index into the @e methods array of a policy, or into the
 * @e authentication_methods array of a backup state.
 */
struct ANASTASIS_MethodIndex
{
  /**
   * The index.
   */
  unsigned int idx;
};


/**
 * Bitmask selecting which optional identity attributes were used.
 */
struct ANASTASIS_AttributeMask
{
  /**
   * The mask.
   */
  uint64_t mask;
};


/* ************************ Geography ******************************** */

/**
 * A continent the user may select.
 */
struct ANASTASIS_ReduxContinent
{
  /**
   * Name of the continent, e.g. "Europe".
   */
  char *name;

  /**
   * Translations of @e name, NULL if not given.  An object mapping
   * language tags to strings, kept verbatim: the reducer only carries
   * translations from the resource files to the user interface and
   * never interprets them.
   */
  json_t *name_i18n;

  /**
   * True if this continent was given as a bare string rather than as
   * an object.  Both forms occur: the reducer itself emits objects,
   * but hand-written states use plain names.
   */
  bool bare;
};


/**
 * A country the user may select.  Copied verbatim from
 * `contrib/redux.countries.json`.
 */
struct ANASTASIS_ReduxCountry
{
  /**
   * Two-letter country code, e.g. "ch".
   */
  char *code;

  /**
   * Name of the country.
   */
  char *name;

  /**
   * Continent the country is on.
   */
  char *continent;

  /**
   * International dialling prefix, e.g. "+41".
   */
  char *call_code;

  /**
   * Translations of @e name, NULL if not given.  Kept verbatim, see
   * #ANASTASIS_ReduxContinent.
   */
  json_t *name_i18n;

  /**
   * Translations of @e continent, NULL if not given.  Kept verbatim,
   * see #ANASTASIS_ReduxContinent.
   */
  json_t *continent_i18n;

  /**
   * Currency used in this country, NULL if not given.  Carried through
   * for the benefit of user interfaces; the reducer does not use it.
   */
  char *currency;
};


/**
 * Specification of one identity attribute we ask the user for.
 * Copied verbatim from `contrib/redux.$CC.json`.
 */
struct ANASTASIS_ReduxAttributeSpec
{
  /**
   * Type of the attribute, e.g. "string" or "date".
   */
  char *type;

  /**
   * Name of the attribute; the key under which the user's answer
   * appears in @e identity_attributes.
   */
  char *name;

  /**
   * Human-readable label.
   */
  char *label;

  /**
   * Widget the GTK UI should use to ask for this attribute.
   */
  char *widget;

  /**
   * UUID identifying this attribute.
   */
  char *uuid;

  /**
   * Tooltip for the UI, NULL if not given.
   */
  char *tooltip;

  /**
   * Regular expression the value must match, NULL if not given.
   */
  char *validation_regex;

  /**
   * Name of a validation function, resolved against the fixed table in
   * reducer/validation.c.  NULL if not given.
   */
  char *validation_logic;

  /**
   * Input mask for autocompletion, NULL if not given.
   */
  char *autocomplete;

  /**
   * Translations of @e label, NULL if not given.  Kept verbatim, see
   * #ANASTASIS_ReduxContinent.
   */
  json_t *label_i18n;

  /**
   * True if the user may leave this attribute blank.
   */
  bool optional;

  /**
   * True if @e optional was explicitly present in the JSON.  Needed so
   * that serialization does not invent an `"optional": false` field
   * where the resource file had none.
   */
  bool have_optional;
};


/* ************************ Providers ******************************** */

/**
 * Status of an Anastasis provider in the reducer state.
 */
enum ANASTASIS_ReduxProviderStatus
{
  /**
   * We have not (yet) talked to this provider.
   */
  ANASTASIS_RPS_NOT_CONTACTED = 0,

  /**
   * We obtained the provider's /config successfully.
   */
  ANASTASIS_RPS_OK = 1,

  /**
   * The provider was taken out of consideration, typically because an
   * upload to it failed.  Note that any configuration we obtained
   * earlier is retained (and re-serialized), matching the behaviour of
   * the JSON-based implementation this replaced.
   */
  ANASTASIS_RPS_DISABLED = 2,

  /**
   * Talking to the provider failed.
   */
  ANASTASIS_RPS_ERROR = 3
};


/**
 * One authentication method offered by a provider, with its price.
 */
struct ANASTASIS_ReduxMethodSpec
{
  /**
   * Type of the method, e.g. "question" or "sms".
   */
  char *type;

  /**
   * Fee charged for using this method during recovery.
   */
  struct TALER_Amount usage_fee;
};


/**
 * Configuration of a provider, as obtained from its /config endpoint.
 */
struct ANASTASIS_ReduxProviderConfig
{
  /**
   * Methods the provider supports.
   */
  struct ANASTASIS_ReduxMethodSpec *methods;

  /**
   * Length of the @e methods array.
   */
  unsigned int methods_len;

  /**
   * Fee for storing a policy for a year.
   */
  struct TALER_Amount annual_fee;

  /**
   * Fee for uploading one truth.
   */
  struct TALER_Amount truth_upload_fee;

  /**
   * Maximum liability the provider accepts for data loss.
   */
  struct TALER_Amount liability_limit;

  /**
   * Salt of this provider.
   */
  struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;

  /**
   * Business name of the provider.
   */
  char *business_name;

  /**
   * Currency the provider charges in, NULL if not given.  Carried
   * through for the benefit of user interfaces.
   */
  char *currency;

  /**
   * Maximum upload size the provider accepts, in megabytes.
   */
  uint32_t storage_limit_in_megabytes;

  /**
   * HTTP status of the /config request.
   */
  uint32_t http_status;
};


/**
 * Why we could not use a provider.
 */
struct ANASTASIS_ReduxProviderError
{
  /**
   * Error code of the failed request.
   */
  enum TALER_ErrorCode ec;

  /**
   * HTTP status of the failed request, 0 if we never got one.
   */
  uint32_t http_status;
};


/**
 * An Anastasis provider we know about.  Serializes as one entry of the
 * `authentication_providers` object, keyed by @e url.
 */
struct ANASTASIS_ReduxProvider
{
  /**
   * URL of the provider; the key this entry is stored under.
   */
  struct ANASTASIS_ProviderUrl url;

  /**
   * What we know about this provider.
   */
  enum ANASTASIS_ReduxProviderStatus status;

  /**
   * True if @e config is valid.  Set whenever a /config reply was
   * obtained, and *not* cleared when @e status becomes
   * #ANASTASIS_RPS_DISABLED.
   */
  bool have_config;

  /**
   * Configuration of the provider; valid iff @e have_config.
   */
  struct ANASTASIS_ReduxProviderConfig config;

  /**
   * Failure details; valid iff @e status is #ANASTASIS_RPS_ERROR.
   */
  struct ANASTASIS_ReduxProviderError error;
};


/* ******************** Backup-specific state ************************ */

/**
 * An authentication method the user configured.
 */
struct ANASTASIS_ReduxAuthMethod
{
  /**
   * Type of the method, e.g. "question".
   */
  char *type;

  /**
   * Instructions to show the user during recovery.
   */
  char *instructions;

  /**
   * The challenge itself (answer, phone number, e-mail address, ...).
   */
  void *challenge;

  /**
   * Number of bytes in @e challenge.
   */
  size_t challenge_size;

  /**
   * MIME type of @e challenge, NULL if not given.
   */
  char *mime_type;
};


/**
 * One member of a policy: authenticate with method
 * @e authentication_method at provider @e provider.
 */
struct ANASTASIS_ReduxPolicyMethod
{
  /**
   * Index into the backup state's @e authentication_methods array.
   */
  struct ANASTASIS_MethodIndex authentication_method;

  /**
   * Provider that stores the key share for this method.
   */
  struct ANASTASIS_ProviderUrl provider;

  /**
   * Truth uploaded for this method, NULL until the upload happened.
   * Owned by this structure; freed with ANASTASIS_truth_free().
   */
  struct ANASTASIS_Truth *truth;

  /**
   * Status of the upload of @e truth; only meaningful if @e truth is
   * non-NULL.
   */
  enum ANASTASIS_UploadStatus upload_status;
};


/**
 * A recovery policy: a set of methods that together recover the secret.
 */
struct ANASTASIS_ReduxPolicy
{
  /**
   * Methods that make up this policy.
   */
  struct ANASTASIS_ReduxPolicyMethod *methods;

  /**
   * Length of the @e methods array.
   */
  unsigned int methods_len;

  /**
   * What recovering via this policy would cost; only valid if
   * @e have_recovery_cost.  Carried through for user interfaces.
   */
  struct TALER_Amount recovery_cost;

  /**
   * True if @e recovery_cost was given.
   */
  bool have_recovery_cost;
};


/**
 * A provider we intend to store the recovery document at.
 */
struct ANASTASIS_ReduxPolicyProvider
{
  /**
   * URL of the provider.
   */
  struct ANASTASIS_ProviderUrl provider_url;

  /**
   * Payment secret remembered from an earlier payment request; only
   * valid if @e have_payment_secret.
   */
  struct ANASTASIS_PaymentSecretP payment_secret;

  /**
   * True if @e payment_secret is set.
   */
  bool have_payment_secret;
};


/**
 * Result of a successful policy upload at one provider.
 */
struct ANASTASIS_ReduxSuccessDetail
{
  /**
   * URL of the provider; the key this entry is stored under.
   */
  struct ANASTASIS_ProviderUrl provider_url;

  /**
   * When the stored policy expires.
   */
  struct GNUNET_TIME_Timestamp policy_expiration;

  /**
   * Version the policy was stored under.
   */
  uint32_t policy_version;
};


/**
 * A payment we are asking the application to make for a policy upload.
 */
struct ANASTASIS_ReduxPolicyPaymentRequest
{
  /**
   * Payment request URI.
   */
  char *payto;

  /**
   * Provider that wants to be paid.
   */
  struct ANASTASIS_ProviderUrl provider;
};


/**
 * Arguments the application passed to the most recent "pay" action.
 */
struct ANASTASIS_ReduxPayArguments
{
  /**
   * How long to wait for the payment to go through.
   */
  struct GNUNET_TIME_Relative timeout;

  /**
   * True if @e timeout was given.
   */
  bool have_timeout;
};


/**
 * The `backup` variant of the reducer state.
 */
struct ANASTASIS_ReduxBackup
{
  /**
   * Which step of the backup process we are at.
   */
  enum ANASTASIS_BackupState state;

  /**
   * Authentication methods the user configured.
   */
  struct ANASTASIS_ReduxAuthMethod *authentication_methods;

  /**
   * Length of the @e authentication_methods array.
   */
  unsigned int authentication_methods_len;

  /**
   * Policies over the @e authentication_methods.
   */
  struct ANASTASIS_ReduxPolicy *policies;

  /**
   * Length of the @e policies array.
   */
  unsigned int policies_len;

  /**
   * Providers we will store the recovery document at.
   */
  struct ANASTASIS_ReduxPolicyProvider *policy_providers;

  /**
   * Length of the @e policy_providers array.
   */
  unsigned int policy_providers_len;

  /**
   * Per-currency total of what the backup will cost.
   */
  struct TALER_Amount *upload_fees;

  /**
   * Length of the @e upload_fees array.
   */
  unsigned int upload_fees_len;

  /**
   * Taler pay URIs the application must settle before we can continue
   * uploading truths.
   */
  char **payments;

  /**
   * Length of the @e payments array.
   */
  unsigned int payments_len;

  /**
   * Payments the application must settle before we can upload the
   * recovery document.
   */
  struct ANASTASIS_ReduxPolicyPaymentRequest *policy_payment_requests;

  /**
   * Length of the @e policy_payment_requests array.
   */
  unsigned int policy_payment_requests_len;

  /**
   * Where the recovery document ended up, per provider.
   */
  struct ANASTASIS_ReduxSuccessDetail *success_details;

  /**
   * Length of the @e success_details array.
   */
  unsigned int success_details_len;

  /**
   * The secret to back up.  Deliberately untyped: its serialization is
   * literally the secret that gets uploaded, so the reducer must never
   * normalize it.  NULL if not (yet) set.
   */
  json_t *core_secret;

  /**
   * Name the user gave the secret, NULL if not set.
   */
  char *secret_name;

  /**
   * When the backup should expire.
   */
  struct GNUNET_TIME_Timestamp expiration;

  /**
   * True if @e expiration is set.
   */
  bool have_expiration;

  /**
   * Arguments of the last "pay" action.
   */
  struct ANASTASIS_ReduxPayArguments pay_arguments;

  /**
   * True if @e pay_arguments is set.
   */
  bool have_pay_arguments;

  /**
   * True if the state has an @e authentication_methods field at all.
   * As in #ANASTASIS_ReduxCommon, presence is tracked separately from
   * the length because an empty array is meaningful.
   */
  bool have_authentication_methods;

  /**
   * True if the state has a @e policies field at all.
   */
  bool have_policies;

  /**
   * True if the state has a @e policy_providers field at all.
   */
  bool have_policy_providers;

  /**
   * True if the state has an @e upload_fees field at all.
   */
  bool have_upload_fees;

  /**
   * True if the state has a @e payments field at all.
   */
  bool have_payments;

  /**
   * True if the state has a @e policy_payment_requests field at all.
   */
  bool have_policy_payment_requests;

  /**
   * True if the state has a @e success_details field at all.
   */
  bool have_success_details;
};


/* ******************* Recovery-specific state *********************** */

/**
 * A challenge the user could solve to recover a key share.
 */
struct ANASTASIS_ReduxChallengeInfo
{
  /**
   * UUID of the truth behind this challenge.
   */
  struct ANASTASIS_CRYPTO_TruthUUIDP uuid;

  /**
   * Type of the challenge, e.g. "question".
   */
  char *type;

  /**
   * Instructions to show the user.
   */
  char *instructions;

  /**
   * Answer the user gave to a secure question, NULL if none.  Kept so
   * that the answer survives the round-trip through the application
   * when the provider demands payment before accepting it.
   */
  char *answer;

  /**
   * Payment secret remembered from a payment request for this
   * challenge; only valid if @e have_payment_secret.
   */
  struct ANASTASIS_PaymentSecretP payment_secret;

  /**
   * True if @e payment_secret is set.
   */
  bool have_payment_secret;
};


/**
 * A decryption policy: solving all of its challenges recovers the
 * secret.
 */
struct ANASTASIS_ReduxRecoveryPolicy
{
  /**
   * UUIDs of the challenges in this policy; each refers to an entry of
   * the recovery information's @e challenges array.
   */
  struct ANASTASIS_CRYPTO_TruthUUIDP *uuids;

  /**
   * Length of the @e uuids array.
   */
  unsigned int uuids_len;
};


/**
 * What we learned about the recovery document we are recovering from.
 */
struct ANASTASIS_ReduxRecoveryInfo
{
  /**
   * All challenges referenced by any policy.
   */
  struct ANASTASIS_ReduxChallengeInfo *challenges;

  /**
   * Length of the @e challenges array.
   */
  unsigned int challenges_len;

  /**
   * Alternative ways to recover the secret.
   */
  struct ANASTASIS_ReduxRecoveryPolicy *policies;

  /**
   * Length of the @e policies array.
   */
  unsigned int policies_len;

  /**
   * Name the user gave the secret, NULL if unknown.
   */
  char *secret_name;

  /**
   * Provider we downloaded the recovery document from.
   */
  struct ANASTASIS_ProviderUrl provider_url;

  /**
   * Version of the recovery document.
   */
  uint32_t version;
};


/**
 * State of a challenge, as reported back to the application.
 */
enum ANASTASIS_ReduxFeedbackStatus
{
  /**
   * The TAN was written to a file (test setups only).
   */
  ANASTASIS_RFS_CODE_IN_FILE = 0,

  /**
   * The TAN was sent to the user out-of-band.
   */
  ANASTASIS_RFS_SEND_TO_ADDRESS = 1,

  /**
   * A Taler payment is required first.
   */
  ANASTASIS_RFS_TALER_PAYMENT = 2,

  /**
   * The provider failed.
   */
  ANASTASIS_RFS_SERVER_FAILURE = 3,

  /**
   * The provider does not know this truth.
   */
  ANASTASIS_RFS_TRUTH_UNKNOWN = 4,

  /**
   * A wire transfer is required first.
   */
  ANASTASIS_RFS_IBAN_INSTRUCTIONS = 5,

  /**
   * The challenge was solved.
   */
  ANASTASIS_RFS_SOLVED = 6,

  /**
   * The answer the user gave was wrong.
   */
  ANASTASIS_RFS_INCORRECT_ANSWER = 7,

  /**
   * Too many attempts.
   */
  ANASTASIS_RFS_RATE_LIMIT_EXCEEDED = 8
};


/**
 * Feedback on one challenge.  Serializes as one entry of the
 * `challenge_feedback` object, keyed by @e uuid.
 */
struct ANASTASIS_ReduxFeedback
{
  /**
   * Challenge this feedback is about; the key this entry is stored
   * under.
   */
  struct ANASTASIS_CRYPTO_TruthUUIDP uuid;

  /**
   * Which variant of @e details is valid.
   */
  enum ANASTASIS_ReduxFeedbackStatus status;

  /**
   * Human-readable, translated hint for the user.  NULL for the
   * variants that do not produce one.
   */
  char *display_hint;

  /**
   * Variant-specific details.
   */
  union
  {

    /**
     * Details for #ANASTASIS_RFS_CODE_IN_FILE.
     */
    struct
    {
      /**
       * File the TAN was written to.
       */
      char *filename;
    } code_in_file;

    /**
     * Details for #ANASTASIS_RFS_SEND_TO_ADDRESS.
     */
    struct
    {
      /**
       * Redacted address the TAN went to, NULL if the provider did not
       * say (because it had already sent the TAN earlier).
       */
      char *address_hint;
    } send_to_address;

    /**
     * Details for #ANASTASIS_RFS_TALER_PAYMENT.
     */
    struct
    {
      /**
       * URI the application must pay.
       */
      char *taler_pay_uri;

      /**
       * Provider that wants to be paid.
       */
      struct ANASTASIS_ProviderUrl provider;

      /**
       * Secret identifying the payment.
       */
      struct ANASTASIS_PaymentSecretP payment_secret;
    } taler_payment;

    /**
     * Details for #ANASTASIS_RFS_SERVER_FAILURE and
     * #ANASTASIS_RFS_TRUTH_UNKNOWN.
     */
    struct
    {
      /**
       * Error code reported.
       */
      enum TALER_ErrorCode ec;

      /**
       * HTTP status reported.
       */
      uint32_t http_status;
    } server_failure;

    /**
     * Details for #ANASTASIS_RFS_IBAN_INSTRUCTIONS.
     */
    struct
    {
      /**
       * IBAN to wire the money to.
       */
      char *target_iban;

      /**
       * Name of the account holder.
       */
      char *target_business_name;

      /**
       * Subject to use for the wire transfer.
       */
      char *wire_transfer_subject;

      /**
       * Amount to wire.
       */
      struct TALER_Amount amount;
    } iban_instructions;

    /**
     * Details for #ANASTASIS_RFS_INCORRECT_ANSWER.
     */
    struct
    {
      /**
       * Error code reported.
       */
      enum TALER_ErrorCode ec;
    } incorrect_answer;

    /**
     * Details for #ANASTASIS_RFS_RATE_LIMIT_EXCEEDED.
     */
    struct
    {
      /**
       * Error code reported.
       */
      enum TALER_ErrorCode ec;

      /**
       * How many attempts are permitted per @e request_frequency.
       */
      uint64_t request_limit;

      /**
       * Length of the rate-limiting window.
       */
      struct GNUNET_TIME_Relative request_frequency;
    } rate_limit_exceeded;

  } details;
};


/**
 * The `recovery` variant of the reducer state.
 */
struct ANASTASIS_ReduxRecovery
{
  /**
   * Which step of the recovery process we are at.
   */
  enum ANASTASIS_RecoveryState state;

  /**
   * The recovery operation itself.  This replaces what used to be the
   * `recovery_document` field: it is deserialized on parse (inert, no
   * network activity until ANASTASIS_recovery_resume() is called) and
   * serialized back on the way out.  NULL before the recovery starts.
   */
  struct ANASTASIS_Recovery *r;

  /**
   * What we told the application about the recovery document, NULL
   * before the recovery starts.
   */
  struct ANASTASIS_ReduxRecoveryInfo *ri;

  /**
   * Feedback on challenges the user tried, keyed by truth UUID.
   */
  struct ANASTASIS_ReduxFeedback *challenge_feedback;

  /**
   * Length of the @e challenge_feedback array.
   */
  unsigned int challenge_feedback_len;

  /**
   * Challenge the user is currently working on; only valid if
   * @e have_selected_challenge.
   */
  struct ANASTASIS_CRYPTO_TruthUUIDP selected_challenge;

  /**
   * True if @e selected_challenge is set.
   */
  bool have_selected_challenge;

  /**
   * True if the state has a @e challenge_feedback field at all.
   */
  bool have_challenge_feedback;

  /**
   * The recovered secret.  Deliberately untyped, see
   * ANASTASIS_ReduxBackup::core_secret.  NULL until recovery succeeds.
   */
  json_t *core_secret;
};


/* ********************** Error state ******************************** */

/**
 * The `error` variant of the reducer state.
 */
struct ANASTASIS_ReduxError
{
  /**
   * The error that occurred.
   */
  enum TALER_ErrorCode code;

  /**
   * Hint derived from @e code, NULL if none.
   */
  char *hint;

  /**
   * Additional detail, NULL if none.
   */
  char *detail;
};


/* ********************** Top-level state **************************** */

/**
 * Which variant of a #ANASTASIS_ReduxState is in use.
 */
enum ANASTASIS_ReduxType
{
  /**
   * A backup is in progress; `reducer_type` is "backup".
   */
  ANASTASIS_RT_BACKUP = 0,

  /**
   * A recovery is in progress; `reducer_type` is "recovery".
   */
  ANASTASIS_RT_RECOVERY = 1,

  /**
   * Something went wrong; `reducer_type` is "error".
   */
  ANASTASIS_RT_ERROR = 2
};


/**
 * Fields shared by the backup and recovery variants.
 */
struct ANASTASIS_ReduxCommon
{
  /**
   * Continents the user may pick from.
   */
  struct ANASTASIS_ReduxContinent *continents;

  /**
   * Length of the @e continents array.
   */
  unsigned int continents_len;

  /**
   * Countries on the selected continent.
   */
  struct ANASTASIS_ReduxCountry *countries;

  /**
   * Length of the @e countries array.
   */
  unsigned int countries_len;

  /**
   * Identity attributes we need from the user in the selected country.
   */
  struct ANASTASIS_ReduxAttributeSpec *required_attributes;

  /**
   * Length of the @e required_attributes array.
   */
  unsigned int required_attributes_len;

  /**
   * Providers we know about, keyed by URL.
   */
  struct ANASTASIS_ReduxProvider *providers;

  /**
   * Length of the @e providers array.
   */
  unsigned int providers_len;

  /**
   * The user's answers to @e required_attributes.  Deliberately
   * untyped: this object is dumped with sorted keys and PoW-hashed to
   * derive the account key, so the reducer must not normalize it.  NULL
   * if the user has not answered yet.
   */
  json_t *identity_attributes;

  /**
   * Continent the user selected, NULL if none.
   */
  char *selected_continent;

  /**
   * Country the user selected, NULL if none.
   */
  char *selected_country;

  /**
   * True if the state has a @e continents field at all.  An array can
   * legitimately be present but empty, which is different from being
   * absent, so presence is tracked separately from the length.
   */
  bool have_continents;

  /**
   * True if the state has a @e countries field at all.
   */
  bool have_countries;

  /**
   * True if the state has a @e required_attributes field at all.
   */
  bool have_required_attributes;

  /**
   * True if the state has an @e authentication_providers field at all.
   */
  bool have_providers;

  /**
   * Currencies in play, carried through for user interfaces.
   */
  char **currencies;

  /**
   * Length of the @e currencies array.
   */
  unsigned int currencies_len;

  /**
   * True if the state has a @e currencies field at all.
   */
  bool have_currencies;
};


/**
 * A fully parsed reducer state.
 */
struct ANASTASIS_ReduxState
{
  /**
   * Which variant of @e details is valid.
   */
  enum ANASTASIS_ReduxType type;

  /**
   * Fields common to the backup and recovery variants; unused when
   * @e type is #ANASTASIS_RT_ERROR.
   */
  struct ANASTASIS_ReduxCommon common;

  /**
   * Variant-specific state.
   */
  union
  {

    /**
     * Valid if @e type is #ANASTASIS_RT_BACKUP.
     */
    struct ANASTASIS_ReduxBackup backup;

    /**
     * Valid if @e type is #ANASTASIS_RT_RECOVERY.
     */
    struct ANASTASIS_ReduxRecovery recovery;

    /**
     * Valid if @e type is #ANASTASIS_RT_ERROR.
     */
    struct ANASTASIS_ReduxError error;

  } details;
};


/* ******************* The three generic routines ******************** */

/**
 * Parse @a json into a typed reducer state.  This is the only routine
 * that reads the JSON encoding of a state.
 *
 * Parsing is strict: unknown fields, missing mandatory fields and
 * values of the wrong type all fail.
 *
 * @param json the state to parse
 * @param[out] ec set to the error that occurred, on failure
 * @param[out] detail set to a static string naming the offending field,
 *             or NULL; never needs to be freed
 * @return NULL on failure, otherwise the parsed state, to be freed with
 *         ANASTASIS_REDUX_state_free_()
 */
struct ANASTASIS_ReduxState *
ANASTASIS_REDUX_state_parse_ (const json_t *json,
                              enum TALER_ErrorCode *ec,
                              const char **detail);


/**
 * Serialize @a rs back into its JSON encoding.  This is the only
 * routine that writes the JSON encoding of a state.
 *
 * @param rs the state to serialize
 * @return the JSON encoding, caller must json_decref() it;
 *         NULL on failure
 */
json_t *
ANASTASIS_REDUX_state_serialize_ (const struct ANASTASIS_ReduxState *rs);


/**
 * Release all resources held by @a rs, including any live
 * `struct ANASTASIS_Recovery` and `struct ANASTASIS_Truth` handles.
 *
 * @param[in] rs the state to free, may be NULL
 */
void
ANASTASIS_REDUX_state_free_ (struct ANASTASIS_ReduxState *rs);


/**
 * Serialize @a rs, hand the result to @a cb, and free @a rs.  This is
 * how every action returns its result: it guarantees that the JSON
 * encoding is produced in exactly one place and that the state is not
 * leaked on any path.
 *
 * @param[in] rs state to return and free
 * @param cb callback to invoke
 * @param cb_cls closure for @a cb
 * @param ec error code to report alongside the state
 */
void
ANASTASIS_REDUX_return_ (struct ANASTASIS_ReduxState *rs,
                         ANASTASIS_ActionCallback cb,
                         void *cb_cls,
                         enum TALER_ErrorCode ec);


/**
 * Report @a ec to @a cb and free @a rs.  The counterpart of
 * #ANASTASIS_REDUX_return_ for the failure path: the reducer replies
 * with a freshly built `error` state, so whatever @a rs held is
 * discarded rather than serialized.
 *
 * @param[in] rs state to free, may be NULL
 * @param cb callback to invoke
 * @param cb_cls closure for @a cb
 * @param ec error to report
 * @param detail human-readable detail, may be NULL
 */
void
ANASTASIS_REDUX_fail_ (struct ANASTASIS_ReduxState *rs,
                       ANASTASIS_ActionCallback cb,
                       void *cb_cls,
                       enum TALER_ErrorCode ec,
                       const char *detail);


/* #ANASTASIS_REDUX_StateCallback, the internal counterpart of
   #ANASTASIS_ActionCallback, is declared in `anastasis_api_redux.h`. */


/* ************************ Small helpers **************************** */

/**
 * Find the provider with the given @a url in @a common.
 *
 * @param common state to search
 * @param url provider URL to look for
 * @return NULL if not found
 */
struct ANASTASIS_ReduxProvider *
ANASTASIS_REDUX_provider_find_ (const struct ANASTASIS_ReduxCommon *common,
                                const char *url);


/**
 * Drop all providers from @a common, freeing them.  Used when the set
 * of applicable providers changes wholesale, e.g. because the user
 * picked a different country.
 *
 * @param[in,out] common state to clear
 */
void
ANASTASIS_REDUX_providers_clear_ (struct ANASTASIS_ReduxCommon *common);


/**
 * Free the contents of @a cfg and zero it.
 *
 * @param[in,out] cfg provider configuration to clear
 */
void
ANASTASIS_REDUX_provider_config_clear_ (
  struct ANASTASIS_ReduxProviderConfig *cfg);


/**
 * Drop all continents from @a common, freeing them.
 *
 * @param[in,out] common state to clear
 */
void
ANASTASIS_REDUX_continents_clear_ (struct ANASTASIS_ReduxCommon *common);


/**
 * Drop all countries from @a common, freeing them.
 *
 * @param[in,out] common state to clear
 */
void
ANASTASIS_REDUX_countries_clear_ (struct ANASTASIS_ReduxCommon *common);


/**
 * Drop all required attributes from @a common, freeing them.
 *
 * @param[in,out] common state to clear
 */
void
ANASTASIS_REDUX_required_attributes_clear_ (
  struct ANASTASIS_ReduxCommon *common);


/* ****************** Importing from resource files ****************** */

/*
 * The reducer's resource files (`redux.countries.json`, `redux.$CC.json`)
 * and the `add_provider` action supply data in exactly the JSON encoding
 * the parser already understands.  Rather than have a second, subtly
 * different reader for them, the relevant parser fragments are exposed
 * here.  Each replaces whatever the state held before.
 */

/**
 * Replace the countries of @a common with those in @a arr.
 *
 * @param[in,out] common state to update
 * @param arr array of countries in the state's JSON encoding
 * @param[out] detail set to the offending field on failure
 * @return #GNUNET_OK on success
 */
enum GNUNET_GenericReturnValue
ANASTASIS_REDUX_countries_set_ (struct ANASTASIS_ReduxCommon *common,
                                const json_t *arr,
                                const char **detail);


/**
 * Replace the required attributes of @a common with those in @a arr.
 *
 * @param[in,out] common state to update
 * @param arr array of attribute specifications in the state's JSON
 *        encoding
 * @param[out] detail set to the offending field on failure
 * @return #GNUNET_OK on success
 */
enum GNUNET_GenericReturnValue
ANASTASIS_REDUX_required_attributes_set_ (struct ANASTASIS_ReduxCommon *common,
                                          const json_t *arr,
                                          const char **detail);


/**
 * Replace the entry for provider @a url in @a common with @a val,
 * adding the provider if it was not known yet.
 *
 * @param[in,out] common state to update
 * @param url provider the entry is about
 * @param val the entry in the state's JSON encoding
 * @param[out] detail set to the offending field on failure
 * @return #GNUNET_OK on success
 */
enum GNUNET_GenericReturnValue
ANASTASIS_REDUX_provider_set_ (struct ANASTASIS_ReduxCommon *common,
                               const char *url,
                               const json_t *val,
                               const char **detail);


/**
 * Find, or create, the provider with the given @a url in @a common.
 *
 * @param[in,out] common state to search and possibly grow
 * @param url provider URL to look for
 * @return the existing or newly added (and #ANASTASIS_RPS_NOT_CONTACTED)
 *         provider entry
 */
struct ANASTASIS_ReduxProvider *
ANASTASIS_REDUX_provider_get_ (struct ANASTASIS_ReduxCommon *common,
                               const char *url);


/**
 * Find the feedback for challenge @a uuid in @a rr.
 *
 * @param rr recovery state to search
 * @param uuid challenge to look for
 * @return NULL if not found
 */
struct ANASTASIS_ReduxFeedback *
ANASTASIS_REDUX_feedback_find_ (const struct ANASTASIS_ReduxRecovery *rr,
                                const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid);


/**
 * Find, or create, the feedback entry for challenge @a uuid in @a rr.
 * An existing entry is reset (its variant-specific fields freed) so
 * that the caller can fill in a fresh variant.
 *
 * @param[in,out] rr recovery state to search and possibly grow
 * @param uuid challenge to look for
 * @return the entry to fill in
 */
struct ANASTASIS_ReduxFeedback *
ANASTASIS_REDUX_feedback_get_ (struct ANASTASIS_ReduxRecovery *rr,
                               const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid);


/**
 * Free the variant-specific contents of @a fb (including the display
 * hint), but not @a fb itself, and zero them.  Used both when freeing
 * a state and when overwriting a feedback entry with a new variant.
 *
 * @param[in,out] fb feedback to reset
 */
void
ANASTASIS_REDUX_feedback_clear_ (struct ANASTASIS_ReduxFeedback *fb);


/**
 * Find the challenge with the given @a uuid in the recovery
 * information of @a rr.
 *
 * @param rr recovery state to search
 * @param uuid challenge to look for
 * @return NULL if not found
 */
struct ANASTASIS_ReduxChallengeInfo *
ANASTASIS_REDUX_challenge_find_ (const struct ANASTASIS_ReduxRecovery *rr,
                                 const struct
                                 ANASTASIS_CRYPTO_TruthUUIDP *uuid);


/**
 * Free the contents of the policy @a p and zero it.
 *
 * @param[in,out] p policy to clear
 */
void
ANASTASIS_REDUX_policy_clear_ (struct ANASTASIS_ReduxPolicy *p);


/**
 * Drop all policies from @a backup, freeing them.
 *
 * @param[in,out] backup state to clear
 */
void
ANASTASIS_REDUX_policies_clear_ (struct ANASTASIS_ReduxBackup *backup);


/**
 * Drop all policy providers from @a backup, freeing them.
 *
 * @param[in,out] backup state to clear
 */
void
ANASTASIS_REDUX_policy_providers_clear_ (struct ANASTASIS_ReduxBackup *backup);


/**
 * Drop the pending truth payments from @a backup, freeing them, and
 * mark the field as absent.
 *
 * @param[in,out] backup state to clear
 */
void
ANASTASIS_REDUX_payments_clear_ (struct ANASTASIS_ReduxBackup *backup);


/**
 * Drop the pending policy payments from @a backup, freeing them, and
 * mark the field as absent.
 *
 * @param[in,out] backup state to clear
 */
void
ANASTASIS_REDUX_policy_payment_requests_clear_ (
  struct ANASTASIS_ReduxBackup *backup);


/**
 * Free the recovery information @a ri.
 *
 * @param[in] ri recovery information to free, may be NULL
 */
void
ANASTASIS_REDUX_recovery_info_free_ (struct ANASTASIS_ReduxRecoveryInfo *ri);


/**
 * Free the contents of @a pu and set it to NULL.
 *
 * @param[in,out] pu provider URL to clear
 */
void
ANASTASIS_REDUX_provider_url_clear_ (struct ANASTASIS_ProviderUrl *pu);


/**
 * Set @a pu to a copy of @a url, freeing any previous value.
 *
 * @param[in,out] pu provider URL to set
 * @param url the URL to copy
 */
void
ANASTASIS_REDUX_provider_url_set_ (struct ANASTASIS_ProviderUrl *pu,
                                   const char *url);


#endif

/* end of anastasis_api_redux_state.h */
