gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-anastasis] branch master updated: -API break: allow clients to sp


From: gnunet
Subject: [taler-anastasis] branch master updated: -API break: allow clients to specify number of years of service to pay for, including reducer update. Will break stuff, partial patch for #6841
Date: Sun, 11 Jul 2021 12:16:06 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository anastasis.

The following commit(s) were added to refs/heads/master by this push:
     new 6ae716e  -API break: allow clients to specify number of years of 
service to pay for, including reducer update. Will break stuff, partial patch 
for #6841
6ae716e is described below

commit 6ae716e2661284378af8708c364ff8b957f7d975
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Jul 11 12:16:03 2021 +0200

    -API break: allow clients to specify number of years of service to pay for, 
including reducer update. Will break stuff, partial patch for #6841
---
 src/backend/anastasis-httpd_policy.c        |   2 +
 src/backend/anastasis-httpd_policy_upload.c | 105 ++++-
 src/backend/anastasis-httpd_truth_upload.c  | 170 +++++--
 src/include/anastasis.h                     |  38 +-
 src/include/anastasis_database_plugin.h     |   2 +
 src/include/anastasis_service.h             |  13 +-
 src/lib/anastasis_backup.c                  |  49 +-
 src/reducer/anastasis_api_backup_redux.c    | 692 +++++++++++++++++++++++-----
 src/restclient/anastasis_api_policy_store.c |  18 +-
 src/restclient/anastasis_api_truth_store.c  |  11 +-
 src/stasis/plugin_anastasis_postgres.c      |  14 +
 11 files changed, 860 insertions(+), 254 deletions(-)

diff --git a/src/backend/anastasis-httpd_policy.c 
b/src/backend/anastasis-httpd_policy.c
index 588ce6a..2417e15 100644
--- a/src/backend/anastasis-httpd_policy.c
+++ b/src/backend/anastasis-httpd_policy.c
@@ -170,9 +170,11 @@ AH_policy_get (struct MHD_Connection *connection,
   enum ANASTASIS_DB_AccountStatus as;
   MHD_RESULT ret;
   uint32_t version;
+  struct GNUNET_TIME_Absolute expiration;
 
   as = db->lookup_account (db->cls,
                            account_pub,
+                           &expiration,
                            &recovery_data_hash,
                            &version);
   switch (as)
diff --git a/src/backend/anastasis-httpd_policy_upload.c 
b/src/backend/anastasis-httpd_policy_upload.c
index b15edab..9104b3b 100644
--- a/src/backend/anastasis-httpd_policy_upload.c
+++ b/src/backend/anastasis-httpd_policy_upload.c
@@ -121,6 +121,18 @@ struct PolicyUploadContext
    */
   struct GNUNET_TIME_Absolute timeout;
 
+  /**
+   * How long must the account be valid?  Determines whether we should
+   * trigger payment, and if so how much.
+   */
+  struct GNUNET_TIME_Absolute end_date;
+
+  /**
+   * How long is the account already valid?
+   * Determines how much the user needs to pay.
+   */
+  struct GNUNET_TIME_Absolute paid_until;
+
   /**
    * Expected total upload size.
    */
@@ -404,11 +416,27 @@ check_payment_cb (void *cls,
   case TALER_MERCHANT_OSC_PAID:
     {
       enum GNUNET_DB_QueryStatus qs;
+      unsigned int years;
+      struct GNUNET_TIME_Relative paid_until;
+
+      years = TALER_amount_divide2 (&osr->details.paid.deposit_total,
+                                    &AH_annual_fee);
+      paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
+                                                  years);
+      /* add 1 week grace period, otherwise if a user
+         wants to pay for 1 year, the first seconds
+         would have passed between making the payment
+         and our subsequent check if +1 year was
+         paid... So we actually say 1 year = 52 weeks
+         on the server, while the client calculates
+         with 365 days. */
+      paid_until = GNUNET_TIME_relative_add (paid_until,
+                                             GNUNET_TIME_UNIT_WEEKS);
 
       qs = db->increment_lifetime (db->cls,
                                    &puc->account,
                                    &puc->payment_identifier,
-                                   GNUNET_TIME_UNIT_YEARS);
+                                   paid_until);
       if (0 <= qs)
         return; /* continue as planned */
       GNUNET_break (0);
@@ -503,20 +531,44 @@ begin_payment (struct PolicyUploadContext *puc)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Suspending connection while creating order at `%s'\n",
               AH_backend_url);
-  MHD_suspend_connection (puc->con);
-
   {
     char *order_id;
+    struct GNUNET_TIME_Absolute now;
+    unsigned int years_to_pay;
+    struct TALER_Amount upload_fee;
+
+    now = GNUNET_TIME_absolute_get ();
+    if (puc->paid_until.abs_value_us < now.abs_value_us)
+      puc->paid_until = now;
+    years_to_pay = (puc->end_date.abs_value_us
+                    - puc->paid_until.abs_value_us)
+                   / GNUNET_TIME_UNIT_YEARS.rel_value_us;
+    if (0 >
+        TALER_amount_multiply (&upload_fee,
+                               &AH_annual_fee,
+                               years_to_pay))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (puc->con,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                         "storage_duration_years");
+    }
 
     order_id = GNUNET_STRINGS_data_to_string_alloc (
       &puc->payment_identifier,
       sizeof(struct ANASTASIS_PaymentSecretP));
-    order = json_pack ("{s:o, s:s, s:s }",
-                       "amount", TALER_JSON_from_amount (&AH_annual_fee),
-                       "summary", "annual fee for anastasis service",
+    order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s }",
+                       "amount", TALER_JSON_from_amount (&upload_fee),
+                       "summary", "Anastasis policy storage fee",
+                       "products",
+                       "description", "policy storage fee",
+                       "quantity", (json_int_t) years_to_pay,
+                       "unit", "years",
                        "order_id", order_id);
     GNUNET_free (order_id);
   }
+  MHD_suspend_connection (puc->con);
   puc->po = TALER_MERCHANT_orders_post2 (AH_ctx,
                                          AH_backend_url,
                                          order,
@@ -817,13 +869,35 @@ AH_handler_policy_post (
 
     /* check if the client insists on paying */
     {
-      const char *order_req;
+      const char *req;
+      unsigned int years;
 
-      order_req = MHD_lookup_connection_value (connection,
-                                               MHD_GET_ARGUMENT_KIND,
-                                               "pay");
-      if (NULL != order_req)
-        return begin_payment (puc);
+      req = MHD_lookup_connection_value (connection,
+                                         MHD_GET_ARGUMENT_KIND,
+                                         "storage_duration");
+      if (NULL != req)
+      {
+        char dummy;
+
+        if (1 != sscanf (req,
+                         "%u%c",
+                         &years,
+                         &dummy))
+        {
+          GNUNET_break_op (0);
+          return TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                             "storage_duration (must be 
non-negative number)");
+        }
+      }
+      else
+      {
+        years = 0;
+      }
+      puc->end_date = GNUNET_TIME_relative_to_absolute (
+        GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
+                                       years));
     }
 
     /* Check if existing policy matches upload (and if, skip it) */
@@ -834,8 +908,15 @@ AH_handler_policy_post (
 
       as = db->lookup_account (db->cls,
                                account_pub,
+                               &puc->paid_until,
                                &hc,
                                &version);
+      if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) &&
+           (puc->paid_until.abs_value_us < puc->end_date.abs_value_us) )
+      {
+        /* user requested extension, force payment */
+        as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED;
+      }
       switch (as)
       {
       case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
diff --git a/src/backend/anastasis-httpd_truth_upload.c 
b/src/backend/anastasis-httpd_truth_upload.c
index 852ddc9..6038bd5 100644
--- a/src/backend/anastasis-httpd_truth_upload.c
+++ b/src/backend/anastasis-httpd_truth_upload.c
@@ -31,6 +31,10 @@
 #include <taler/taler_signatures.h>
 #include "anastasis_authorization_lib.h"
 
+/**
+ * For how many years do we allow users to store truth at most?
+ */
+#define MAX_YEARS_STORAGE 5
 
 /**
  * Information we track per truth upload.
@@ -83,11 +87,21 @@ struct TruthUploadContext
    */
   struct GNUNET_TIME_Absolute timeout;
 
+  /**
+   * Fee that is to be paid for this upload.
+   */
+  struct TALER_Amount upload_fee;
+
   /**
    * HTTP response code to use on resume, if resp is set.
    */
   unsigned int response_code;
 
+  /**
+   * For how many years must the customer still pay?
+   */
+  unsigned int years_to_pay;
+
 };
 
 
@@ -300,16 +314,33 @@ check_payment_cb (void *cls,
     case TALER_MERCHANT_OSC_PAID:
       {
         enum GNUNET_DB_QueryStatus qs;
-
-        qs = db->record_truth_upload_payment (db->cls,
-                                              &tuc->truth_uuid,
-                                              &AH_truth_upload_fee,
-                                              GNUNET_TIME_UNIT_YEARS);
+        unsigned int years;
+        struct GNUNET_TIME_Relative paid_until;
+
+        years = TALER_amount_divide2 (&osr->details.paid.deposit_total,
+                                      &AH_truth_upload_fee);
+        paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
+                                                    years);
+        /* add 1 week grace period, otherwise if a user
+           wants to pay for 1 year, the first seconds
+           would have passed between making the payment
+           and our subsequent check if +1 year was
+           paid... So we actually say 1 year = 52 weeks
+           on the server, while the client calculates
+           with 365 days. */
+        paid_until = GNUNET_TIME_relative_add (paid_until,
+                                               GNUNET_TIME_UNIT_WEEKS);
+        qs = db->record_truth_upload_payment (
+          db->cls,
+          &tuc->truth_uuid,
+          &osr->details.paid.deposit_total,
+          paid_until);
         if (qs <= 0)
         {
           tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
-          tuc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
-                                            "record_truth_upload_payment");
+          tuc->resp = TALER_MHD_make_error (
+            TALER_EC_GENERIC_DB_STORE_FAILED,
+            "record_truth_upload_payment");
           break;
         }
       }
@@ -353,11 +384,16 @@ check_payment_cb (void *cls,
                   "%u, setting up fresh order %s\n",
                   MHD_HTTP_NOT_FOUND,
                   order_id);
-      order = json_pack ("{s:o, s:s, s:s}",
+      order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s}",
                          "amount",
-                         TALER_JSON_from_amount (&AH_truth_upload_fee),
+                         TALER_JSON_from_amount (&tuc->upload_fee),
                          "summary",
                          "Anastasis challenge storage fee",
+                         "products",
+                         "description", "challenge storage fee",
+                         "quantity", (json_int_t) tuc->years_to_pay,
+                         "unit", "years",
+
                          "order_id",
                          order_id);
       GNUNET_free (order_id);
@@ -465,6 +501,7 @@ AH_handler_truth_post (
   const char *truth_mime;
   const char *type;
   enum GNUNET_DB_QueryStatus qs;
+  uint32_t storage_years;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("keyshare_data",
                                  &keyshare_data),
@@ -475,6 +512,8 @@ AH_handler_truth_post (
                               &encrypted_truth_size),
     GNUNET_JSON_spec_string ("truth_mime",
                              &truth_mime),
+    GNUNET_JSON_spec_uint32 ("storage_duration_years",
+                             &storage_years),
     GNUNET_JSON_spec_end ()
   };
 
@@ -553,44 +592,6 @@ AH_handler_truth_post (
       }
     }
 
-    {
-      struct TALER_Amount zero_amount;
-
-      TALER_amount_get_zero (AH_currency,
-                             &zero_amount);
-      if (0 != TALER_amount_cmp (&AH_truth_upload_fee,
-                                 &zero_amount))
-      {
-        struct GNUNET_TIME_Absolute paid_until;
-        enum GNUNET_DB_QueryStatus qs;
-
-        qs = db->check_truth_upload_paid (db->cls,
-                                          truth_uuid,
-                                          &paid_until);
-        if (qs < 0)
-          return TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                             NULL);
-        if ( (0 == qs) ||
-             (0 ==
-              GNUNET_TIME_absolute_get_remaining (paid_until).rel_value_us) )
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                      "Truth upload payment required (%d)!\n",
-                      qs);
-          return begin_payment (tuc);
-        }
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "TRUTH paid until %s (%d)!\n",
-                    GNUNET_STRINGS_relative_time_to_string (
-                      GNUNET_TIME_absolute_get_remaining (
-                        paid_until),
-                      GNUNET_YES),
-                    qs);
-      }
-    }
-
   } /* end 'if (NULL == tuc)' */
 
   if (NULL != tuc->resp)
@@ -651,6 +652,81 @@ AH_handler_truth_post (
     }
   }
 
+  if (storage_years > MAX_YEARS_STORAGE)
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                       "storage_duration_years");
+  }
+
+  {
+    struct TALER_Amount zero_amount;
+
+    TALER_amount_get_zero (AH_currency,
+                           &zero_amount);
+    if (0 != TALER_amount_cmp (&AH_truth_upload_fee,
+                               &zero_amount))
+    {
+      struct GNUNET_TIME_Absolute paid_until;
+      struct GNUNET_TIME_Absolute desired_until;
+      enum GNUNET_DB_QueryStatus qs;
+
+      desired_until
+        = GNUNET_TIME_relative_to_absolute (
+            GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
+                                           storage_years));
+      qs = db->check_truth_upload_paid (db->cls,
+                                        truth_uuid,
+                                        &paid_until);
+      if (qs < 0)
+        return TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                           NULL);
+      if ( (0 == qs) ||
+           (paid_until.abs_value_us < desired_until.abs_value_us) )
+      {
+        struct GNUNET_TIME_Absolute now;
+
+        now = GNUNET_TIME_absolute_get ();
+        if (paid_until.abs_value_us < now.abs_value_us)
+          paid_until = now;
+        tuc->years_to_pay = (desired_until.abs_value_us
+                             - paid_until.abs_value_us)
+                            / GNUNET_TIME_UNIT_YEARS.rel_value_us;
+        if (0 >
+            TALER_amount_multiply (&tuc->upload_fee,
+                                   &AH_truth_upload_fee,
+                                   tuc->years_to_pay))
+        {
+          GNUNET_break_op (0);
+          return TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                             "storage_duration_years");
+        }
+        if ( (0 != tuc->upload_fee.fraction) ||
+             (0 != tuc->upload_fee.value) )
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Truth upload payment required (%d)!\n",
+                      qs);
+          return begin_payment (tuc);
+        }
+      }
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "TRUTH paid until %s (%d)!\n",
+                  GNUNET_STRINGS_relative_time_to_string (
+                    GNUNET_TIME_absolute_get_remaining (
+                      paid_until),
+                    GNUNET_YES),
+                  qs);
+    }
+  }
+
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Storing truth!\n");
   qs = db->store_truth (db->cls,
diff --git a/src/include/anastasis.h b/src/include/anastasis.h
index 1e8c1af..705dd07 100644
--- a/src/include/anastasis.h
+++ b/src/include/anastasis.h
@@ -613,11 +613,11 @@ typedef void
  * @param mime_type format of the challenge
  * @param provider_salt the providers salt
  * @param truth_data contains the truth for this challenge i.e. phone number, 
email address
- * @param truth_data_size size of the data
- * @param payment_requested true if the client wants to pay more for the 
account now
+ * @param truth_data_size size of the @a truth_data
+ * @param payment_years_requested for how many years would the client like the 
service to store the truth?
  * @param pay_timeout how long to wait for payment
  * @param tc opens the truth callback which contains the status of the upload
- * @param tc_cls closure for the callback
+ * @param tc_cls closure for the @a tc callback
  */
 struct ANASTASIS_TruthUpload *
 ANASTASIS_truth_upload (
@@ -630,7 +630,7 @@ ANASTASIS_truth_upload (
   const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
   const void *truth_data,
   size_t truth_data_size,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   struct GNUNET_TIME_Relative pay_timeout,
   ANASTASIS_TruthCallback tc,
   void *tc_cls);
@@ -648,8 +648,8 @@ ANASTASIS_truth_upload (
  * @param mime_type format of the challenge
  * @param provider_salt the providers salt
  * @param truth_data contains the truth for this challenge i.e. phone number, 
email address
- * @param truth_data_size size of the data
- * @param payment_requested true if the client wants to pay more for the 
account now
+ * @param truth_data_size size of the @a truth_data
+ * @param payment_years_requested for how many years would the client like the 
service to store the truth?
  * @param pay_timeout how long to wait for payment
  * @param nonce nonce to use for symmetric encryption
  * @param uuid truth UUID to use
@@ -657,7 +657,7 @@ ANASTASIS_truth_upload (
  * @param truth_key symmetric encryption key to use to encrypt @a truth_data
  * @param key_share share of the overall key to store in this truth object
  * @param tc opens the truth callback which contains the status of the upload
- * @param tc_cls closure for the callback
+ * @param tc_cls closure for the @a tc callback
  */
 struct ANASTASIS_TruthUpload *
 ANASTASIS_truth_upload2 (
@@ -670,7 +670,7 @@ ANASTASIS_truth_upload2 (
   const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
   const void *truth_data,
   size_t truth_data_size,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   struct GNUNET_TIME_Relative pay_timeout,
   const struct ANASTASIS_CRYPTO_NonceP *nonce,
   const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
@@ -690,11 +690,11 @@ ANASTASIS_truth_upload2 (
  * @param user_id user identifier derived from user data and backend salt
  * @param[in] t truth details, reference is consumed
  * @param truth_data contains the truth for this challenge i.e. phone number, 
email address
- * @param truth_data_size size of the data
- * @param payment_requested true if the client wants to pay more for the 
account now
+ * @param truth_data_size size of the @a truth_data
+ * @param payment_years_requested for how many years would the client like the 
service to store the truth?
  * @param pay_timeout how long to wait for payment
  * @param tc opens the truth callback which contains the status of the upload
- * @param tc_cls closure for the callback
+ * @param tc_cls closure for the @a tc callback
  */
 struct ANASTASIS_TruthUpload *
 ANASTASIS_truth_upload3 (struct GNUNET_CURL_Context *ctx,
@@ -702,7 +702,7 @@ ANASTASIS_truth_upload3 (struct GNUNET_CURL_Context *ctx,
                          struct ANASTASIS_Truth *t,
                          const void *truth_data,
                          size_t truth_data_size,
-                         bool payment_requested,
+                         uint32_t payment_years_requested,
                          struct GNUNET_TIME_Relative pay_timeout,
                          ANASTASIS_TruthCallback tc,
                          void *tc_cls);
@@ -923,16 +923,16 @@ struct ANASTASIS_ProviderDetails
  * @param providers array of providers with URLs to upload the policies to
  * @param pss_length length of the @a providers array
  * @param policies list of policies which are included in this recovery 
document
- * @param policies_length amount of policies in the document
- * @param payment_requested the client insists on paying for the policy store, 
even if not
- *                 yet needed
+ * @param policies_length length of the @a policies array
+ * @param payment_years_requested for how many years would the client like the 
service to store the truth?
  * @param pay_timeout how long to wait for payment
  * @param spc payment callback is opened to pay the upload
- * @param spc_cls closure for the payment callback
+ * @param spc_cls closure for the @a spc payment callback
  * @param src callback for the upload process
- * @param src_cls closure for the upload callback
+ * @param src_cls closure for the @a src upload callback
  * @param core_secret input of the user which is secured by anastasis e.g. 
(wallet private key)
- * @param core_secret_size size of the core secret
+ * @param core_secret_size size of the @a core_secret
+ * @return NULL on error
  */
 struct ANASTASIS_SecretShare *
 ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
@@ -941,7 +941,7 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
                         unsigned int pss_length,
                         const struct ANASTASIS_Policy *policies[],
                         unsigned int policies_len,
-                        bool payment_requested,
+                        uint32_t payment_years_requested,
                         struct GNUNET_TIME_Relative pay_timeout,
                         ANASTASIS_ShareResultCallback src,
                         void *src_cls,
diff --git a/src/include/anastasis_database_plugin.h 
b/src/include/anastasis_database_plugin.h
index 8bd702e..b7ae2dc 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -380,6 +380,7 @@ struct ANASTASIS_DatabasePlugin
    *
    * @param cls closure
    * @param anastasis_pub account identifier
+   * @param[out] paid_until until when is the account paid up?
    * @param[out] recovery_data_hash set to hash of @a recovery document
    * @param[out] version set to the recovery policy version
    * @return transaction status
@@ -388,6 +389,7 @@ struct ANASTASIS_DatabasePlugin
   (*lookup_account)(
     void *cls,
     const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub,
+    struct GNUNET_TIME_Absolute *paid_until,
     struct GNUNET_HashCode *recovery_data_hash,
     uint32_t *version);
 
diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h
index 0af787f..1a61e54 100644
--- a/src/include/anastasis_service.h
+++ b/src/include/anastasis_service.h
@@ -387,7 +387,7 @@ typedef void
  * @param anastasis_priv private key of the user's account
  * @param recovery_data policy data to be stored
  * @param recovery_data_size number of bytes in @a recovery_data
- * @param payment_requested true if the client wants to pay more for the 
account now
+ * @param payment_years_requested for how many years would the client like the 
service to store the truth?
  * @param paid_order_id payment identifier of last payment
  * @param payment_timeout how long to wait for the payment, use
  *           #GNUNET_TIME_UNIT_ZERO to let the server pick
@@ -402,7 +402,7 @@ ANASTASIS_policy_store (
   const struct ANASTASIS_CRYPTO_AccountPrivateKeyP *anastasis_priv,
   const void *recovery_data,
   size_t recovery_data_size,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   const struct ANASTASIS_PaymentSecretP *payment_secret,
   struct GNUNET_TIME_Relative payment_timeout,
   ANASTASIS_PolicyStoreCallback cb,
@@ -667,7 +667,7 @@ typedef void
  * @param truth_mime mime type of @e encrypted_truth (after decryption)
  * @param encrypted_truth_size number of bytes in @e encrypted_truth
  * @param encrypted_truth contains the @a type-specific authorization data
- * @param payment_requested true if we want to pay, even if not yet required
+ * @param payment_years_requested for how many years would the client like the 
service to store the truth?
  * @param payment_timeout how long to wait for the payment, use
  *           #GNUNET_TIME_UNIT_ZERO to let the server pick
  * @param cb callback processing the response from /truth
@@ -684,7 +684,7 @@ ANASTASIS_truth_store (
   const char *truth_mime,
   size_t encrypted_truth_size,
   const void *encrypted_truth,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   struct GNUNET_TIME_Relative payment_timeout,
   ANASTASIS_TruthStoreCallback cb,
   void *cb_cls);
@@ -704,8 +704,9 @@ ANASTASIS_truth_store_cancel (
 // FIXME: BAD API & MISSING IMPLEMENTATION:
 
 /**
- * Defines a Callback for a Escrow Provider Inspection, simply passes back the 
terms
- * of the provider (price, supported methods .... )
+ * Defines a Callback for a Escrow Provider Inspection, simply passes
+ * back the configuration of the provider (price, supported methods
+ * .... )
  *
  * @param cls closure for the callback
  * @param policy 0 terminated string which contains the terms of service
diff --git a/src/lib/anastasis_backup.c b/src/lib/anastasis_backup.c
index 2e00a2b..c3eaed9 100644
--- a/src/lib/anastasis_backup.c
+++ b/src/lib/anastasis_backup.c
@@ -80,14 +80,6 @@ struct ANASTASIS_Truth
 };
 
 
-/**
- * Extracts truth data from JSON.
- *
- * @param json JSON encoding to decode; truth returned ONLY valid as long
- *             as the JSON remains valid (do not decref until the truth
- *             is truly finished)
- * @return decoded truth object, NULL on error
- */
 struct ANASTASIS_Truth *
 ANASTASIS_truth_from_json (const json_t *json)
 {
@@ -139,15 +131,6 @@ ANASTASIS_truth_from_json (const json_t *json)
 }
 
 
-/**
- * Returns JSON-encoded truth data.
- * Creates a policy with a set of truth's.  Creates the policy key
- * with the different key shares from the @a truths. The policy key
- * will then be used to encrypt/decrypt the escrow master key.
- *
- * @param t object to return JSON encoding for
- * @return JSON encoding of @a t
- */
 json_t *
 ANASTASIS_truth_to_json (const struct ANASTASIS_Truth *t)
 {
@@ -234,29 +217,13 @@ truth_store_callback (void *cls,
 }
 
 
-/**
- * Retries upload of truth data to an escrow provider using an
- * existing truth object. If payment is required, it is requested via
- * the @a tc callback.
- *
- * @param ctx the CURL context used to connect to the backend
- * @param user_id user identifier derived from user data and backend salt
- * @param[in] t truth details, reference is consumed
- * @param truth_data contains the truth for this challenge i.e. phone number, 
email address
- * @param truth_data_size size of the data
- * @param payment_requested true if the client wants to pay more for the 
account now
- * @param paid_order_id payment identifier of last payment
- * @param pay_timeout how long to wait for payment
- * @param tc opens the truth callback which contains the status of the upload
- * @param tc_cls closure for the callback
- */
 struct ANASTASIS_TruthUpload *
 ANASTASIS_truth_upload3 (struct GNUNET_CURL_Context *ctx,
                          const struct ANASTASIS_CRYPTO_UserIdentifierP 
*user_id,
                          struct ANASTASIS_Truth *t,
                          const void *truth_data,
                          size_t truth_data_size,
-                         bool payment_requested,
+                         uint32_t payment_years_requested,
                          struct GNUNET_TIME_Relative pay_timeout,
                          ANASTASIS_TruthCallback tc,
                          void *tc_cls)
@@ -316,7 +283,7 @@ ANASTASIS_truth_upload3 (struct GNUNET_CURL_Context *ctx,
                                    t->mime_type,
                                    encrypted_truth_size,
                                    encrypted_truth,
-                                   payment_requested,
+                                   payment_years_requested,
                                    pay_timeout,
                                    &truth_store_callback,
                                    tu);
@@ -343,7 +310,7 @@ ANASTASIS_truth_upload2 (
   const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
   const void *truth_data,
   size_t truth_data_size,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   struct GNUNET_TIME_Relative pay_timeout,
   const struct ANASTASIS_CRYPTO_NonceP *nonce,
   const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
@@ -375,7 +342,7 @@ ANASTASIS_truth_upload2 (
                                   t,
                                   truth_data,
                                   truth_data_size,
-                                  payment_requested,
+                                  payment_years_requested,
                                   pay_timeout,
                                   tc,
                                   tc_cls);
@@ -393,7 +360,7 @@ ANASTASIS_truth_upload (
   const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
   const void *truth_data,
   size_t truth_data_size,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   struct GNUNET_TIME_Relative pay_timeout,
   ANASTASIS_TruthCallback tc,
   void *tc_cls)
@@ -426,7 +393,7 @@ ANASTASIS_truth_upload (
                                   provider_salt,
                                   truth_data,
                                   truth_data_size,
-                                  payment_requested,
+                                  payment_years_requested,
                                   pay_timeout,
                                   &nonce,
                                   &uuid,
@@ -750,7 +717,7 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
                         unsigned int pss_length,
                         const struct ANASTASIS_Policy *policies[],
                         unsigned int policies_len,
-                        bool payment_requested,
+                        uint32_t payment_years_requested,
                         struct GNUNET_TIME_Relative pay_timeout,
                         ANASTASIS_ShareResultCallback src,
                         void *src_cls,
@@ -964,7 +931,7 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
       &anastasis_priv,
       recovery_data,
       recovery_data_size,
-      payment_requested,
+      payment_years_requested,
       (! GNUNET_is_zero (&pss->payment_secret))
       ? &pss->payment_secret
       : NULL,
diff --git a/src/reducer/anastasis_api_backup_redux.c 
b/src/reducer/anastasis_api_backup_redux.c
index 60430d7..b6f5176 100644
--- a/src/reducer/anastasis_api_backup_redux.c
+++ b/src/reducer/anastasis_api_backup_redux.c
@@ -41,6 +41,59 @@ static const char *backup_strings[] = {
 #undef GENERATE_STRING
 
 
+/**
+ * Linked list of costs.
+ */
+struct Costs
+{
+
+  /**
+   * Kept in a LL.
+   */
+  struct Costs *next;
+
+  /**
+   * Cost in one of the currencies.
+   */
+  struct TALER_Amount cost;
+};
+
+
+/**
+ * Add amount from @a cost to @a my_cost list.
+ *
+ * @param[in,out] my_cost pointer to list to modify
+ * @param cost amount to add
+ */
+static void
+add_cost (struct Costs **my_cost,
+          const struct TALER_Amount *cost)
+{
+  for (struct Costs *pos = *my_cost;
+       NULL != pos;
+       pos = pos->next)
+  {
+    if (GNUNET_OK !=
+        TALER_amount_cmp_currency (&pos->cost,
+                                   cost))
+      continue;
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&pos->cost,
+                                     &pos->cost,
+                                     cost));
+    return;
+  }
+  {
+    struct Costs *nc;
+
+    nc = GNUNET_new (struct Costs);
+    nc->cost = *cost;
+    nc->next = *my_cost;
+    *my_cost = nc;
+  }
+}
+
+
 enum ANASTASIS_BackupState
 ANASTASIS_backup_state_from_string_ (const char *state_string)
 {
@@ -380,24 +433,6 @@ del_authentication (json_t *state,
 
 /* ********************** done_authentication ******************** */
 
-/**
- * Linked list of costs to associate with a policy.
- */
-struct Costs
-{
-
-  /**
-   * Kept in a LL.
-   */
-  struct Costs *next;
-
-  /**
-   * Cost in one of the currencies.
-   */
-  struct TALER_Amount cost;
-};
-
-
 /**
  * Which provider would be used for the given challenge,
  * and at what cost?
@@ -1113,41 +1148,6 @@ lookup_salt (const json_t *state,
 }
 
 
-/**
- * Add costs from @a pe to @a my_cost list.
- *
- * @param[in,out] my_cost pointer to list to modify
- * @param pe cost entry to add
- */
-static void
-add_cost (struct Costs **my_cost,
-          const struct PolicyEntry *pe)
-{
-  for (struct Costs *pos = *my_cost;
-       NULL != pos;
-       pos = pos->next)
-  {
-    if (GNUNET_OK !=
-        TALER_amount_cmp_currency (&pos->cost,
-                                   &pe->usage_fee))
-      continue;
-    GNUNET_assert (0 <=
-                   TALER_amount_add (&pos->cost,
-                                     &pos->cost,
-                                     &pe->usage_fee));
-    return;
-  }
-  {
-    struct Costs *nc;
-
-    nc = GNUNET_new (struct Costs);
-    nc->cost = pe->usage_fee;
-    nc->next = *my_cost;
-    *my_cost = nc;
-  }
-}
-
-
 /**
  * Compare two cost lists.
  *
@@ -1296,7 +1296,7 @@ evaluate_map (struct PolicyBuilder *pb,
       if (found)
         continue; /* cost already included, do not add */
       add_cost (&my_cost,
-                pe);
+                &pe->usage_fee);
     }
   }
 
@@ -2234,6 +2234,233 @@ del_challenge (json_t *state,
 /* ********************** done_policy_review ***************** */
 
 
+/**
+ * Calculate how many years of service we need
+ * from the desired @a expiration time,
+ * rounding up.
+ *
+ * @param expiration desired expiration time
+ * @return number of years of service to pay for
+*/
+static unsigned int
+expiration_to_years (struct GNUNET_TIME_Absolute expiration)
+{
+  struct GNUNET_TIME_Relative rem;
+  unsigned int years;
+
+  rem = GNUNET_TIME_absolute_get_remaining (expiration);
+  years = rem.rel_value_us / GNUNET_TIME_UNIT_YEARS.rel_value_us;
+  if (0 != rem.rel_value_us / GNUNET_TIME_UNIT_YEARS.rel_value_us)
+    years++;
+  return years;
+}
+
+
+/**
+ * Update @a state such that the earliest expiration for
+ * any truth or policy is @a expiration. Recalculate
+ * the ``upload_fees`` array with the associated costs.
+ *
+ * @param[in,out] state our state to update
+ * @param expiration new expiration to enforce
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if the state is invalid
+ */
+static enum GNUNET_GenericReturnValue
+update_expiration_cost (json_t *state,
+                        struct GNUNET_TIME_Absolute expiration)
+{
+  struct Costs *costs = NULL;
+  unsigned int years;
+  json_t *providers;
+
+  providers = json_object_get (state,
+                               "policy_providers");
+  if (0 == json_array_size (providers))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  years = expiration_to_years (expiration);
+
+  /* go over all providers and add up cost */
+  {
+    const char *url;
+    json_t *provider;
+
+    json_object_foreach (providers, url, provider)
+    {
+      struct TALER_Amount annual_fee;
+      struct GNUNET_JSON_Specification pspec[] = {
+        TALER_JSON_spec_amount ("annual_fee",
+                                &annual_fee),
+        GNUNET_JSON_spec_end ()
+      };
+      struct TALER_Amount fee;
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (provider,
+                             pspec,
+                             NULL, NULL))
+      {
+        GNUNET_break (0);
+        return GNUNET_SYSERR;
+      }
+      if (0 >
+          TALER_amount_multiply (&fee,
+                                 &annual_fee,
+                                 years))
+      {
+        GNUNET_break (0);
+        return GNUNET_SYSERR;
+      }
+      add_cost (&costs,
+                &fee);
+    }
+  }
+
+  /* go over all truths and add up cost */
+  {
+    unsigned int off = 0;
+    unsigned int len = 0;
+    struct AlreadySeen
+    {
+      uint32_t method;
+      const char *provider_url;
+    } *seen;
+    json_t *policies;
+    size_t pidx;
+    json_t *policy;
+
+    policies = json_object_get (state,
+                                "policies");
+    json_array_foreach (policies, pidx, policy)
+    {
+      json_t *methods;
+      json_t *method;
+      size_t midx;
+
+      methods = json_object_get (policy,
+                                 "methods");
+      json_array_foreach (methods, midx, method)
+      {
+        const char *provider_url;
+        uint32_t method_idx;
+
+        struct GNUNET_JSON_Specification spec[] = {
+          GNUNET_JSON_spec_string ("provider",
+                                   &provider_url),
+          GNUNET_JSON_spec_uint32 ("authentication_method",
+                                   &method_idx),
+          GNUNET_JSON_spec_end ()
+        };
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (method,
+                               spec,
+                               NULL, NULL))
+        {
+          GNUNET_break (0);
+          return GNUNET_SYSERR;
+        }
+        /* check if we have seen this one before */
+        {
+          bool found = false;
+
+          for (unsigned int i = 0; i<off; i++)
+            if ( (seen[i].method == method_idx) &&
+                 (0 == strcmp (seen[i].provider_url,
+                               provider_url)) )
+              found = true;
+          if (found)
+            continue; /* skip */
+        }
+        if (off == len)
+        {
+          GNUNET_array_grow (seen,
+                             len,
+                             4 + len * 2);
+        }
+        seen[off].method = method_idx;
+        seen[off].provider_url = provider_url;
+        off++;
+        {
+          struct TALER_Amount upload_cost;
+          struct GNUNET_JSON_Specification pspec[] = {
+            TALER_JSON_spec_amount ("truth_upload_fee",
+                                    &upload_cost),
+            GNUNET_JSON_spec_end ()
+          };
+          struct TALER_Amount fee;
+          const json_t *provider_cfg
+            = json_object_get (providers,
+                               provider_url);
+
+          if (GNUNET_OK !=
+              GNUNET_JSON_parse (provider_cfg,
+                                 pspec,
+                                 NULL, NULL))
+          {
+            GNUNET_break (0);
+            return GNUNET_SYSERR;
+          }
+          if (0 >
+              TALER_amount_multiply (&fee,
+                                     &upload_cost,
+                                     years))
+          {
+            GNUNET_break (0);
+            return GNUNET_SYSERR;
+          }
+          add_cost (&costs,
+                    &fee);
+        }
+      }
+    }
+    GNUNET_array_grow (seen,
+                       len,
+                       0);
+  }
+
+  /* update 'expiration' in state */
+  {
+    json_t *eo;
+
+    eo = GNUNET_JSON_from_time_abs (expiration);
+    GNUNET_assert (0 ==
+                   json_object_set_new (state,
+                                        "expiration",
+                                        eo));
+  }
+
+  /* convert 'costs' into state */
+  {
+    json_t *arr;
+
+    arr = json_array ();
+    GNUNET_assert (NULL != arr);
+    while (NULL != costs)
+    {
+      struct Costs *nxt = costs->next;
+      json_t *ao;
+
+      ao = TALER_JSON_from_amount (&costs->cost);
+      GNUNET_assert (0 ==
+                     json_array_append_new (arr,
+                                            ao));
+      GNUNET_free (costs);
+      costs = nxt;
+    }
+    GNUNET_assert (0 ==
+                   json_object_set_new (state,
+                                        "upload_fees",
+                                        arr));
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * DispatchHandler/Callback function which is called for a
  * "done_policy_review" action.
@@ -2262,6 +2489,40 @@ done_policy_review (json_t *state,
                            "no policies specified");
     return NULL;
   }
+  {
+    struct GNUNET_TIME_Absolute exp = {0};
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_absolute_time ("expiration",
+                                        &exp)),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (state,
+                           spec,
+                           NULL, NULL))
+    {
+      ANASTASIS_redux_fail_ (cb,
+                             cb_cls,
+                             
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
+                             "invalid expiration specified");
+      return NULL;
+    }
+    if (0 == exp.abs_value_us)
+      exp = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS);
+
+    if (GNUNET_OK !=
+        update_expiration_cost (state,
+                                exp))
+    {
+      ANASTASIS_redux_fail_ (cb,
+                             cb_cls,
+                             
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
+                             "could not calculate expiration cost");
+      return NULL;
+    }
+  }
   set_state (state,
              ANASTASIS_BACKUP_STATE_SECRET_EDITING);
   cb (cb_cls,
@@ -2412,6 +2673,11 @@ struct UploadContext
    */
   struct GNUNET_TIME_Relative timeout;
 
+  /**
+   * For how many years should we pay?
+   */
+  unsigned int years;
+
 };
 
 
@@ -2666,16 +2932,12 @@ share_secret (struct UploadContext *uc)
   void *core_secret;
   json_t *jpolicies;
   json_t *providers = NULL;
-  bool force_payment = false;
-  unsigned int policies_len;
+  size_t policies_len;
   unsigned int pds_len;
   struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("identity_attributes",
                            &user_id),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_bool ("force_pay",
-                             &force_payment)),
     GNUNET_JSON_spec_json ("policies",
                            &jpolicies),
     GNUNET_JSON_spec_json ("policy_providers",
@@ -2768,7 +3030,7 @@ share_secret (struct UploadContext *uc)
     memset (pds,
             0,
             sizeof (pds));
-    for (unsigned int i = 0; i<policies_len; i++)
+    for (size_t i = 0; i<policies_len; i++)
     {
       const json_t *policy = json_array_get (jpolicies,
                                              i);
@@ -2941,7 +3203,7 @@ share_secret (struct UploadContext *uc)
                                      pds_len,
                                      policies,
                                      policies_len,
-                                     force_payment,
+                                     uc->years,
                                      timeout,
                                      &secret_share_result_cb,
                                      uc,
@@ -3120,7 +3382,6 @@ add_truth_object (struct UploadContext *uc,
     }
   }
 
-
   if (NULL == tue)
   {
     /* Create new entry */
@@ -3181,13 +3442,9 @@ add_truth_object (struct UploadContext *uc,
   {
     struct ANASTASIS_CRYPTO_ProviderSaltP salt;
     struct ANASTASIS_CRYPTO_UserIdentifierP id;
-    bool force_payment = false;
     void *truth_data;
     size_t truth_data_size;
     struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_bool ("force_pay",
-                               &force_payment)),
       GNUNET_JSON_spec_varsize ("challenge",
                                 &truth_data,
                                 &truth_data_size),
@@ -3232,7 +3489,7 @@ add_truth_object (struct UploadContext *uc,
                                        tue->t,
                                        truth_data,
                                        truth_data_size,
-                                       force_payment,
+                                       uc->years,
                                        uc->timeout,
                                        &truth_upload_cb,
                                        tue);
@@ -3323,15 +3580,11 @@ check_truth_upload (struct UploadContext *uc,
     const char *type;
     const char *mime_type = NULL;
     const char *instructions = NULL;
-    bool force_payment = false;
     void *truth_data;
     size_t truth_data_size;
     struct GNUNET_JSON_Specification spec[] = {
       GNUNET_JSON_spec_string ("type",
                                &type),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_bool ("force_pay",
-                               &force_payment)),
       GNUNET_JSON_spec_mark_optional (
         GNUNET_JSON_spec_string ("mime_type",
                                  &mime_type)),
@@ -3415,7 +3668,7 @@ check_truth_upload (struct UploadContext *uc,
                                           &provider_salt,
                                           truth_data,
                                           truth_data_size,
-                                          force_payment,
+                                          uc->years,
                                           uc->timeout,
                                           &truth_upload_cb,
                                           tue);
@@ -3431,7 +3684,7 @@ check_truth_upload (struct UploadContext *uc,
                                            &provider_salt,
                                            truth_data,
                                            truth_data_size,
-                                           force_payment,
+                                           uc->years,
                                            uc->timeout,
                                            &nonce,
                                            &uuid,
@@ -3473,7 +3726,24 @@ upload (json_t *state,
   struct UploadContext *uc;
   json_t *auth_methods;
   json_t *policies;
+  struct GNUNET_TIME_Absolute expiration;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_absolute_time ("expiration",
+                                    &expiration),
+    GNUNET_JSON_spec_end ()
+  };
 
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (state,
+                         spec,
+                         NULL, NULL))
+  {
+    ANASTASIS_redux_fail_ (cb,
+                           cb_cls,
+                           TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+                           "'expiration' missing");
+    return NULL;
+  }
   auth_methods = json_object_get (state,
                                   "authentication_methods");
   if ( (! json_is_array (auth_methods)) ||
@@ -3503,6 +3773,7 @@ upload (json_t *state,
   uc->cb = cb;
   uc->cb_cls = cb_cls;
   uc->state = json_incref (state);
+  uc->years = expiration_to_years (expiration);
 
   {
     json_t *args;
@@ -3689,15 +3960,68 @@ core_secret_fits (const json_t *state,
 }
 
 
+/**
+ * Check if the upload size limit is satisfied.
+ *
+ * @param state our state
+ * @param secret_size size of the uploaded secret
+ * @return #GNUNET_OK if @a secret_size works for all providers,
+ *     #GNUNET_NO if the @a secret_size is too big,
+ *     #GNUNET_SYSERR if a provider has a limit of 0
+ */
+static enum GNUNET_GenericReturnValue
+check_upload_size_limit (json_t *state,
+                         size_t secret_size)
+{
+  uint32_t min_limit = UINT32_MAX;
+  json_t *aps = json_object_get (state,
+                                 "authentication_providers");
+  const char *url;
+  json_t *ap;
+
+  /* We calculate the minimum upload limit of all possible providers;
+     this is under the (simplified) assumption that we store the
+     recovery document at all providers; this may be changed later,
+     see #6760. */
+  json_object_foreach (aps, url, ap)
+  {
+    uint32_t limit;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
+                               &limit),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (ap,
+                           spec,
+                           NULL, NULL))
+    {
+      /* skip malformed provider, likely /config failed */
+      continue;
+    }
+    if (0 == limit)
+      return GNUNET_SYSERR;
+    min_limit = GNUNET_MIN (min_limit,
+                            limit);
+  }
+  if (! core_secret_fits (state,
+                          secret_size,
+                          min_limit))
+    return GNUNET_NO;
+  return GNUNET_OK;
+}
+
+
 /**
  * DispatchHandler/Callback function which is called for a
  * "enter_secret" action.
- * Returns an #ANASTASIS_ReduxAction as operation is async.
  *
  * @param state state to operate on
  * @param arguments arguments to use for operation on state
  * @param cb callback to call during/after operation
  * @param cb_cls callback closure
+ * @return NULL
  */
 static struct ANASTASIS_ReduxAction *
 enter_secret (json_t *state,
@@ -3708,9 +4032,17 @@ enter_secret (json_t *state,
   json_t *jsecret;
   void *secret;
   size_t secret_size;
+  const char *secret_name = NULL;
+  struct GNUNET_TIME_Absolute expiration = {0};
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("secret",
                            &jsecret),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_string ("name",
+                               &secret_name)),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_absolute_time ("expiration",
+                                      &expiration)),
     GNUNET_JSON_spec_end ()
   };
 
@@ -3740,65 +4072,179 @@ enter_secret (json_t *state,
 
   /* check upload size limit */
   {
-    uint32_t min_limit = UINT32_MAX;
-    json_t *aps = json_object_get (state,
-                                   "authentication_providers");
-    const char *url;
-    json_t *ap;
-
-    /* We calculate the minimum upload limit of all possible providers;
-       this is under the (simplified) assumption that we store the
-       recovery document at all providers; this may be changed later,
-       see #6760. */
-    json_object_foreach (aps, url, ap)
-    {
-      uint32_t limit;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
-                                 &limit),
-        GNUNET_JSON_spec_end ()
-      };
+    enum GNUNET_GenericReturnValue ret;
 
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (ap,
-                             spec,
-                             NULL, NULL))
-      {
-        /* skip malformed provider, likely /config failed */
-        continue;
-      }
-      if (0 == limit)
-      {
-        ANASTASIS_redux_fail_ (cb,
-                               cb_cls,
-                               TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
-                               "provider has an upload limit of 0");
-        GNUNET_free (secret);
-        return NULL;
-      }
-      min_limit = GNUNET_MIN (min_limit,
-                              limit);
-    }
-    if (! core_secret_fits (state,
-                            secret_size,
-                            min_limit))
+    ret = check_upload_size_limit (state,
+                                   secret_size);
+    switch (ret)
     {
+    case GNUNET_SYSERR:
+      ANASTASIS_redux_fail_ (cb,
+                             cb_cls,
+                             TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+                             "provider has an upload limit of 0");
+      GNUNET_free (secret);
+      return NULL;
+    case GNUNET_NO:
       ANASTASIS_redux_fail_ (cb,
                              cb_cls,
                              TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG,
                              NULL);
       GNUNET_free (secret);
       return NULL;
+    default:
+      break;
     }
   }
-
-
+  if (0 != expiration.abs_value_us)
+  {
+    if (GNUNET_OK !=
+        update_expiration_cost (state,
+                                expiration))
+    {
+      ANASTASIS_redux_fail_ (cb,
+                             cb_cls,
+                             
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
+                             "could not calculate expiration cost");
+      return NULL;
+    }
+  }
+  if (NULL != secret_name)
+    GNUNET_assert (0 ==
+                   json_object_set_new (state,
+                                        "secret_name",
+                                        json_string (secret_name)));
   GNUNET_assert (0 ==
                  json_object_set_new (state,
                                       "core_secret",
                                       GNUNET_JSON_from_data (secret,
                                                              secret_size)));
-  GNUNET_free (secret);
+  GNUNET_JSON_parse_free (spec);
+  return NULL;
+}
+
+
+/**
+ * DispatchHandler/Callback function which is called for the
+ * "update_expiration" action in the "secret editing" state.
+ * Updates how long we are to store the truth and policies
+ * and computes the new cost.
+ *
+ * @param state state to operate on
+ * @param arguments arguments to use for operation on state
+ * @param cb callback to call during/after operation
+ * @param cb_cls callback closure
+ * @return NULL (synchronous operation)
+ */
+static struct ANASTASIS_ReduxAction *
+update_expiration (json_t *state,
+                   const json_t *arguments,
+                   ANASTASIS_ActionCallback cb,
+                   void *cb_cls)
+{
+  struct GNUNET_TIME_Absolute expiration;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_absolute_time ("expiration",
+                                    &expiration),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (NULL == arguments)
+  {
+    ANASTASIS_redux_fail_ (cb,
+                           cb_cls,
+                           TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+                           "arguments missing");
+    return NULL;
+  }
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (arguments,
+                         spec,
+                         NULL, NULL))
+  {
+    ANASTASIS_redux_fail_ (cb,
+                           cb_cls,
+                           TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+                           "'expiration' argument required");
+    return NULL;
+  }
+  if (GNUNET_OK !=
+      update_expiration_cost (state,
+                              expiration))
+  {
+    ANASTASIS_redux_fail_ (cb,
+                           cb_cls,
+                           TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
+                           "could not calculate expiration cost");
+    return NULL;
+  }
+  return NULL;
+}
+
+
+/**
+ * DispatchHandler/Callback function which is called for the
+ * "next" action in the "secret editing" state.
+ * Returns an #ANASTASIS_ReduxAction as operation is async.
+ *
+ * @param state state to operate on
+ * @param arguments arguments to use for operation on state
+ * @param cb callback to call during/after operation
+ * @param cb_cls callback closure
+ */
+static struct ANASTASIS_ReduxAction *
+finish_secret (json_t *state,
+               const json_t *arguments,
+               ANASTASIS_ActionCallback cb,
+               void *cb_cls)
+{
+  void *core_secret;
+  size_t core_secret_size;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_varsize ("core_secret",
+                              &core_secret,
+                              &core_secret_size),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (state,
+                         spec,
+                         NULL, NULL))
+  {
+    ANASTASIS_redux_fail_ (cb,
+                           cb_cls,
+                           TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+                           "State parsing failed: 'core_secret' is missing");
+    return NULL;
+  }
+
+  /* check upload size limit */
+  {
+    enum GNUNET_GenericReturnValue ret;
+
+    ret = check_upload_size_limit (state,
+                                   core_secret_size);
+    switch (ret)
+    {
+    case GNUNET_SYSERR:
+      ANASTASIS_redux_fail_ (cb,
+                             cb_cls,
+                             TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+                             "provider has an upload limit of 0");
+      GNUNET_JSON_parse_free (spec);
+      return NULL;
+    case GNUNET_NO:
+      ANASTASIS_redux_fail_ (cb,
+                             cb_cls,
+                             TALER_EC_ANASTASIS_REDUCER_SECRET_TOO_BIG,
+                             NULL);
+      GNUNET_JSON_parse_free (spec);
+      return NULL;
+    default:
+      break;
+    }
+  }
 
   GNUNET_JSON_parse_free (spec);
   return upload (state,
@@ -3997,6 +4443,16 @@ ANASTASIS_backup_action_ (json_t *state,
       "back",
       &ANASTASIS_back_generic_decrement_
     },
+    {
+      ANASTASIS_BACKUP_STATE_SECRET_EDITING,
+      "update_expiration",
+      &update_expiration
+    },
+    {
+      ANASTASIS_BACKUP_STATE_SECRET_EDITING,
+      "next",
+      &finish_secret
+    },
     {
       ANASTASIS_BACKUP_STATE_TRUTHS_PAYING,
       "pay",
diff --git a/src/restclient/anastasis_api_policy_store.c 
b/src/restclient/anastasis_api_policy_store.c
index babf06e..9b8c370 100644
--- a/src/restclient/anastasis_api_policy_store.c
+++ b/src/restclient/anastasis_api_policy_store.c
@@ -308,7 +308,7 @@ ANASTASIS_policy_store (
   const struct ANASTASIS_CRYPTO_AccountPrivateKeyP *anastasis_priv,
   const void *recovery_data,
   size_t recovery_data_size,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   const struct ANASTASIS_PaymentSecretP *payment_secret,
   struct GNUNET_TIME_Relative payment_timeout,
   ANASTASIS_PolicyStoreCallback cb,
@@ -418,24 +418,30 @@ ANASTASIS_policy_store (
     char *path;
     struct ANASTASIS_CRYPTO_AccountPublicKeyP pub;
     char timeout_ms[32];
+    char pyrs[32];
 
     GNUNET_snprintf (timeout_ms,
                      sizeof (timeout_ms),
                      "%llu",
                      tms);
+    GNUNET_snprintf (pyrs,
+                     sizeof (pyrs),
+                     "%u",
+                     (unsigned int) payment_years_requested);
     GNUNET_CRYPTO_eddsa_key_get_public (&anastasis_priv->priv,
                                         &pub.pub);
-    acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (&pub,
-                                                       sizeof (pub));
+    acc_pub_str
+      = GNUNET_STRINGS_data_to_string_alloc (&pub,
+                                             sizeof (pub));
     GNUNET_asprintf (&path,
                      "policy/%s",
                      acc_pub_str);
     GNUNET_free (acc_pub_str);
     pso->url = TALER_url_join (backend_url,
                                path,
-                               "pay",
-                               (payment_requested)
-                               ? "y"
+                               "storage_duration",
+                               (0 != payment_years_requested)
+                               ? pyrs
                                : NULL,
                                "timeout_ms",
                                (0 != payment_timeout.rel_value_us)
diff --git a/src/restclient/anastasis_api_truth_store.c 
b/src/restclient/anastasis_api_truth_store.c
index 5a75878..20bcde4 100644
--- a/src/restclient/anastasis_api_truth_store.c
+++ b/src/restclient/anastasis_api_truth_store.c
@@ -248,7 +248,7 @@ ANASTASIS_truth_store (
   const char *truth_mime,
   size_t encrypted_truth_size,
   const void *encrypted_truth,
-  bool payment_requested,
+  uint32_t payment_years_requested,
   struct GNUNET_TIME_Relative payment_timeout,
   ANASTASIS_TruthStoreCallback cb,
   void *cb_cls)
@@ -277,8 +277,6 @@ ANASTASIS_truth_store (
                      uuid_str);
     tso->url = TALER_url_join (backend_url,
                                path,
-                               "pay",
-                               (payment_requested) ? "y" : NULL,
                                "timeout_ms",
                                (0 != payment_timeout.rel_value_us)
                                ? timeout_ms
@@ -293,7 +291,8 @@ ANASTASIS_truth_store (
     truth_data = json_pack ("{s:o," /* encrypted KeyShare */
                             " s:s," /* type */
                             " s:o," /* nonce */
-                            " s:s}", /* truth_mime */
+                            " s:s," /* truth_mime */
+                            " s:I}",  /* payment years */
                             "keyshare_data",
                             GNUNET_JSON_from_data_auto (encrypted_keyshare),
                             "type",
@@ -304,7 +303,9 @@ ANASTASIS_truth_store (
                             "truth_mime",
                             (NULL != truth_mime)
                             ? truth_mime
-                            : "");
+                            : "",
+                            "storage_duration_years",
+                            (json_int_t) payment_years_requested);
     GNUNET_assert (NULL != truth_data);
     json_str = json_dumps (truth_data,
                            JSON_COMPACT);
diff --git a/src/stasis/plugin_anastasis_postgres.c 
b/src/stasis/plugin_anastasis_postgres.c
index e183211..ded8d0c 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -1071,9 +1071,12 @@ postgres_get_key_share (
 
 
 /**
+ * Check if an account exists, and if so, return the
+ * current @a recovery_document_hash.
  *
  * @param cls closure
  * @param anastasis_pub account identifier
+ * @param[out] paid_until until when is the account paid up?
  * @param[out] recovery_data_hash set to hash of @a recovery document
  * @param[out] version set to the recovery policy version
  * @return transaction status
@@ -1082,6 +1085,7 @@ enum ANASTASIS_DB_AccountStatus
 postgres_lookup_account (
   void *cls,
   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub,
+  struct GNUNET_TIME_Absolute *paid_until,
   struct GNUNET_HashCode *recovery_data_hash,
   uint32_t *version)
 {
@@ -1096,6 +1100,8 @@ postgres_lookup_account (
   postgres_preflight (pg);
   {
     struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("expiration_date",
+                                            &paid_until),
       GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash",
                                             recovery_data_hash),
       GNUNET_PQ_result_spec_uint32 ("version",
@@ -1124,6 +1130,8 @@ postgres_lookup_account (
   /* check if account exists */
   {
     struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("expiration_date",
+                                            &paid_until),
       GNUNET_PQ_result_spec_end
     };
 
@@ -1144,6 +1152,10 @@ postgres_lookup_account (
     return ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED;
   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     /* indicates: no backup */
+    *version = UINT32_MAX;
+    memset (recovery_data_hash,
+            0,
+            sizeof (*recovery_data_hash));
     return ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS;
   default:
     GNUNET_break (0);
@@ -1898,7 +1910,9 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             "SELECT"
                             " version"
                             ",recovery_data_hash"
+                            ",expiration_date"
                             " FROM anastasis_recoverydocument"
+                            " JOIN anastasis_user USING (user_id)"
                             " WHERE user_id=$1"
                             " ORDER BY version DESC"
                             " LIMIT 1;",

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]