gnunet-svn
[Top][All Lists]
Advanced

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

[taler-anastasis] branch master updated: enable payment for truth upload


From: gnunet
Subject: [taler-anastasis] branch master updated: enable payment for truth upload, misc fixes
Date: Wed, 10 Feb 2021 16:44:43 +0100

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 2368c23  enable payment for truth upload, misc fixes
2368c23 is described below

commit 2368c238c574287fa2fa50ee8da2cbef035b2251
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Wed Feb 10 16:44:41 2021 +0100

    enable payment for truth upload, misc fixes
---
 contrib/gana                                   |   2 +-
 src/backend/anastasis-httpd.c                  |  49 +++
 src/backend/anastasis-httpd.h                  |   8 +-
 src/backend/anastasis-httpd_policy.c           |  24 +-
 src/backend/anastasis-httpd_policy.h           |   2 +
 src/backend/anastasis-httpd_policy_upload.c    | 166 +++-----
 src/backend/anastasis-httpd_truth.c            |  11 +-
 src/backend/anastasis-httpd_truth.h            |  16 +
 src/backend/anastasis-httpd_truth_upload.c     | 547 +++++++++++++++++++++++--
 src/include/anastasis.h                        | 452 +++++++++++---------
 src/include/anastasis_database_plugin.h        |  32 ++
 src/include/anastasis_service.h                | 168 ++++++--
 src/lib/anastasis_recovery.c                   | 186 ++++-----
 src/reducer/anastasis_api_backup_redux.c       |   6 +-
 src/reducer/anastasis_api_redux.c              |  18 +-
 src/restclient/anastasis_api_keyshare_lookup.c | 263 ++++++++++--
 src/restclient/anastasis_api_truth_store.c     |  13 -
 src/stasis/plugin_anastasis_postgres.c         |  82 ++++
 src/stasis/stasis-0001.sql                     |  29 +-
 19 files changed, 1521 insertions(+), 553 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index 5de7cb5..cb43a26 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 5de7cb5209109f97d56f7dd99262f24b0988c6b8
+Subproject commit cb43a2642dd75f21c45392667e0877c97474915b
diff --git a/src/backend/anastasis-httpd.c b/src/backend/anastasis-httpd.c
index 004bc7a..dcb0d3d 100644
--- a/src/backend/anastasis-httpd.c
+++ b/src/backend/anastasis-httpd.c
@@ -47,6 +47,11 @@ unsigned long long int AH_upload_limit_mb;
  */
 struct TALER_Amount AH_annual_fee;
 
+/**
+ * Fee for a truth upload.
+ */
+struct TALER_Amount AH_truth_upload_fee;
+
 /**
  * Amount of insurance.
  */
@@ -358,6 +363,7 @@ url_handler (void *cls,
     if (0 == strcmp (method,
                      MHD_HTTP_METHOD_POST))
     {
+      // FIXME: need to accumulate upload_data first!
       return AH_handler_policy_post (connection,
                                      hc,
                                      &account_pub,
@@ -396,6 +402,7 @@ url_handler (void *cls,
     if (0 == strcmp (method,
                      MHD_HTTP_METHOD_POST))
     {
+      // FIXME: need to accumulate upload_data first!
       return AH_handler_truth_post (connection,
                                     hc,
                                     &tu,
@@ -442,6 +449,7 @@ do_shutdown (void *cls)
   (void) cls;
   AH_resume_all_bc ();
   AH_truth_shutdown ();
+  AH_truth_upload_shutdown ();
   if (NULL != mhd_task)
   {
     GNUNET_SCHEDULER_cancel (mhd_task);
@@ -639,6 +647,18 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
+  if (GNUNET_OK !=
+      TALER_config_get_amount (config,
+                               "anastasis",
+                               "TRUTH_UPLOAD_FEE",
+                               &AH_truth_upload_fee))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "anastasis",
+                               "TRUTH_UPLOAD_FEE");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
   if (GNUNET_OK !=
       TALER_config_get_currency (config,
                                  &AH_currency))
@@ -679,6 +699,35 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
+  if ( (0 != strncasecmp ("https://";,
+                          AH_backend_url,
+                          strlen ("https://";))) &&
+       (0 != strncasecmp ("http://";,
+                          AH_backend_url,
+                          strlen ("http://";))) )
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               "anastasis",
+                               "PAYMENT_BACKEND_URL",
+                               "Must be HTTP(S) URL");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  if ( (0 == strcasecmp ("https://";,
+                         AH_backend_url)) ||
+       (0 == strcasecmp ("http://";,
+                         AH_backend_url)) )
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               "anastasis",
+                               "PAYMENT_BACKEND_URL",
+                               "Must have domain name");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (config,
                                              "anastasis",
diff --git a/src/backend/anastasis-httpd.h b/src/backend/anastasis-httpd.h
index 4a98248..7384fe1 100644
--- a/src/backend/anastasis-httpd.h
+++ b/src/backend/anastasis-httpd.h
@@ -139,11 +139,14 @@ extern unsigned long long AH_upload_limit_mb;
 
 /**
  * Annual fee for the backup account.
- *
- * FIXME: the amount to pay is fixed to this value. Better variable value?
  */
 extern struct TALER_Amount AH_annual_fee;
 
+/**
+ * Fee for a truth upload.
+ */
+extern struct TALER_Amount AH_truth_upload_fee;
+
 /**
  * Amount of insurance.
  */
@@ -189,6 +192,7 @@ extern struct ANASTASIS_CRYPTO_PowSalt AH_server_salt;
  */
 extern struct GNUNET_CURL_Context *AH_ctx;
 
+
 /**
  * Kick MHD to run now, to be called after MHD_resume_connection().
  * Basically, we need to explicitly resume MHD's event loop whenever
diff --git a/src/backend/anastasis-httpd_policy.c 
b/src/backend/anastasis-httpd_policy.c
index 72e74be..4e89bb4 100644
--- a/src/backend/anastasis-httpd_policy.c
+++ b/src/backend/anastasis-httpd_policy.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2019 GNUnet e.V.
+  Copyright (C) 2019, 2021 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -55,6 +55,7 @@ return_policy (struct MHD_Connection *connection,
   struct ANASTASIS_AccountSignatureP account_sig;
   struct GNUNET_HashCode recovery_data_hash;
   const char *version_s;
+  char version_b[14];
   uint32_t version;
   void *res_recovery_data;
   size_t res_recovery_data_size;
@@ -64,9 +65,12 @@ return_policy (struct MHD_Connection *connection,
                                            "version");
   if (NULL != version_s)
   {
+    char dummy;
+
     if (1 != sscanf (version_s,
-                     "%u",
-                     &version))
+                     "%u%c",
+                     &version,
+                     &dummy))
     {
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_REQUEST,
@@ -90,7 +94,11 @@ return_policy (struct MHD_Connection *connection,
                                            &res_recovery_data_size,
                                            &res_recovery_data,
                                            &version);
-
+    GNUNET_snprintf (version_b,
+                     sizeof (version_b),
+                     "%u",
+                     (unsigned int) version);
+    version_s = version_b;
   }
   switch (qs)
   {
@@ -99,13 +107,13 @@ return_policy (struct MHD_Connection *connection,
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       "recovery document");
+                                       "get_recovery_document");
   case GNUNET_DB_STATUS_SOFT_ERROR:
     GNUNET_break (0);
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
-                                       NULL);
+                                       "get_recovery_document");
   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     GNUNET_break (0);
     return TALER_MHD_reply_with_error (connection,
@@ -132,6 +140,10 @@ return_policy (struct MHD_Connection *connection,
                   MHD_add_response_header (resp,
                                            
ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE,
                                            sig_s));
+    GNUNET_break (MHD_YES ==
+                  MHD_add_response_header (resp,
+                                           
ANASTASIS_HTTP_HEADER_POLICY_VERSION,
+                                           version_s));
     GNUNET_break (MHD_YES ==
                   MHD_add_response_header (resp,
                                            MHD_HTTP_HEADER_ETAG,
diff --git a/src/backend/anastasis-httpd_policy.h 
b/src/backend/anastasis-httpd_policy.h
index 6285508..9fb630d 100644
--- a/src/backend/anastasis-httpd_policy.h
+++ b/src/backend/anastasis-httpd_policy.h
@@ -45,6 +45,8 @@ AH_policy_get (struct MHD_Connection *connection,
 
 
 /**
+ * Handle POST /policy/$ACCOUNT_PUB request.
+ *
  * @param connection the MHD connection to handle
  * @param con_cls the connection's closure
  * @param account_pub public key of the account
diff --git a/src/backend/anastasis-httpd_policy_upload.c 
b/src/backend/anastasis-httpd_policy_upload.c
index 744aa87..61decba 100644
--- a/src/backend/anastasis-httpd_policy_upload.c
+++ b/src/backend/anastasis-httpd_policy_upload.c
@@ -44,11 +44,6 @@
 struct PolicyUploadContext
 {
 
-  /**
-   * Context for cleanup logic.
-   */
-  struct TM_HandlerContext hc;
-
   /**
    * Signature of the account holder.
    */
@@ -59,22 +54,12 @@ struct PolicyUploadContext
    */
   struct ANASTASIS_CRYPTO_AccountPublicKeyP account;
 
-  /**
-   * Hash of the previous upload, or zeros if first upload.
-   */
-  struct GNUNET_HashCode old_policy_upload_hash;
-
   /**
    * Hash of the upload we are receiving right now (as promised
    * by the client, to be verified!).
    */
   struct GNUNET_HashCode new_policy_upload_hash;
 
-  /**
-   * The claim token
-   */
-  struct TALER_ClaimTokenP claim_token;
-
   /**
    * Hash context for the upload.
    */
@@ -146,11 +131,6 @@ struct PolicyUploadContext
    */
   unsigned int response_code;
 
-  /**
-   * Whether to generate a claim token.
-   */
-  bool make_claim_token;
-
   /**
    * true if client provided a payment secret / order ID?
    */
@@ -210,6 +190,8 @@ cleanup_ctx (struct TM_HandlerContext *hc)
 
   if (NULL != puc->po)
     TALER_MERCHANT_orders_post_cancel (puc->po);
+  if (NULL != puc->cpo)
+    TALER_MERCHANT_merchant_order_get_cancel (puc->cpo);
   if (NULL != puc->hash_ctx)
     GNUNET_CRYPTO_hash_context_abort (puc->hash_ctx);
   if (NULL != puc->resp)
@@ -428,6 +410,7 @@ check_payment_cb (void *cls,
 
   /* refunds are not supported, verify */
   puc->cpo = NULL;
+  // FIXME: osr could be NULL! check hr first!
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Payment status checked: %s\n",
               osr->status ? "paid" : "unpaid");
@@ -527,13 +510,10 @@ await_payment (struct PolicyUploadContext *puc,
  * if required.
  *
  * @param puc context to begin payment for.
- * @param pay_req #GNUNET_YES if payment was explicitly requested,
- *                #GNUNET_NO if payment is needed
  * @return MHD status code
  */
 static MHD_RESULT
-begin_payment (struct PolicyUploadContext *puc,
-               int pay_req)
+begin_payment (struct PolicyUploadContext *puc)
 {
   json_t *order;
   enum GNUNET_DB_QueryStatus qs;
@@ -618,8 +598,7 @@ prepare_payment (struct PolicyUploadContext *puc)
       GNUNET_CRYPTO_QUALITY_NONCE,
       &puc->payment_identifier,
       sizeof (struct ANASTASIS_PaymentSecretP));
-    return begin_payment (puc,
-                          GNUNET_NO);
+    return begin_payment (puc);
   }
   await_payment (puc,
                  CHECK_PAYMENT_GENERIC_TIMEOUT);
@@ -627,40 +606,6 @@ prepare_payment (struct PolicyUploadContext *puc)
 }
 
 
-/**
- * We got some query status from the DB.  Handle the error cases.
- * May perform asynchronous operations by suspending the connection
- * if required.
- *
- * @param puc connection to handle status for
- * @param qs query status to handle
- * @return #MHD_YES or #MHD_NO
- */
-static MHD_RESULT
-handle_database_error (struct PolicyUploadContext *puc,
-                       enum GNUNET_DB_QueryStatus qs)
-{
-  switch (qs)
-  {
-  case GNUNET_DB_STATUS_HARD_ERROR:
-  case GNUNET_DB_STATUS_SOFT_ERROR:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (puc->con,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       NULL);
-  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-    GNUNET_assert (0);
-    return MHD_NO;
-  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-    GNUNET_assert (0);
-    return MHD_NO;
-  }
-  GNUNET_break (0);
-  return MHD_NO;
-}
-
-
 MHD_RESULT
 AH_handler_policy_post (
   struct MHD_Connection *connection,
@@ -676,7 +621,7 @@ AH_handler_policy_post (
     /* first call, setup internals */
     puc = GNUNET_new (struct PolicyUploadContext);
     hc->ctx = puc;
-    puc->hc.cc = &cleanup_ctx;
+    hc->cc = &cleanup_ctx;
     puc->con = connection;
 
     {
@@ -684,7 +629,7 @@ AH_handler_policy_post (
 
       pay_id = MHD_lookup_connection_value (connection,
                                             MHD_HEADER_KIND,
-                                            "Payment-Identifier");
+                                            
ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER);
       if (NULL != pay_id)
       {
         if (GNUNET_OK !=
@@ -698,7 +643,8 @@ AH_handler_policy_post (
           return TALER_MHD_reply_with_error (connection,
                                              MHD_HTTP_BAD_REQUEST,
                                              
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                             "Payment-Identifier header must 
be a base32-encoded Payment-Secret");
+                                             
ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER
+                                             " header must be a base32-encoded 
Payment-Secret");
         }
         puc->payment_identifier_provided = true;
       }
@@ -722,8 +668,8 @@ AH_handler_policy_post (
           connection,
           MHD_HTTP_BAD_REQUEST,
           (NULL == lens)
-          ? TALER_EC_ANASTASIS_POLICY_MISSING_CONTENT_LENGTH
-          : TALER_EC_ANASTASIS_POLICY_MALFORMED_CONTENT_LENGTH,
+          ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH
+          : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH,
           NULL);
       }
       if (len / 1024 / 1024 >= AH_upload_limit_mb)
@@ -795,7 +741,6 @@ AH_handler_policy_post (
       struct ANASTASIS_UploadSignaturePS usp = {
         .purpose.size = htonl (sizeof (usp)),
         .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD),
-        // usp.old_recovery_data_hash = puc->old_policy_upload_hash,
         .new_recovery_data_hash = puc->new_policy_upload_hash
       };
 
@@ -829,8 +774,10 @@ AH_handler_policy_post (
                                            &paid,
                                            &valid_counter);
         if (qs < 0)
-          return handle_database_error (puc,
-                                        qs);
+          return TALER_MHD_reply_with_error (puc->con,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                             NULL);
 
         if ( (! paid) || (! valid_counter) )
         {
@@ -842,8 +789,7 @@ AH_handler_policy_post (
               &puc->payment_identifier,
               sizeof (struct ANASTASIS_PaymentSecretP));
           }
-          return begin_payment (puc,
-                                GNUNET_YES);
+          return begin_payment (puc);
         }
       }
 
@@ -855,14 +801,12 @@ AH_handler_policy_post (
         TALER_amount_get_zero (AH_currency,
                                &zero_amount);
         /* generate fresh payment identifier */
-        GNUNET_CRYPTO_random_block (
-          GNUNET_CRYPTO_QUALITY_STRONG,
-          &puc->payment_identifier,
-          sizeof (struct ANASTASIS_PaymentSecretP));
+        GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
+                                    &puc->payment_identifier,
+                                    sizeof (struct ANASTASIS_PaymentSecretP));
         if (0 != TALER_amount_cmp (&AH_annual_fee,
                                    &zero_amount))
-          return begin_payment (puc,
-                                GNUNET_YES);
+          return begin_payment (puc);
         /* Cost is zero, fake "zero" payment having happened */
         qs = db->record_recdoc_payment (db->cls,
                                         account_pub,
@@ -870,15 +814,19 @@ AH_handler_policy_post (
                                         &puc->payment_identifier,
                                         &AH_annual_fee);
         if (qs <= 0)
-          return handle_database_error (puc,
-                                        qs);
+          return TALER_MHD_reply_with_error (puc->con,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                             NULL);
         qs = db->increment_lifetime (db->cls,
                                      account_pub,
                                      &puc->payment_identifier,
                                      GNUNET_TIME_UNIT_YEARS);
         if (qs <= 0)
-          return handle_database_error (puc,
-                                        qs);
+          return TALER_MHD_reply_with_error (puc->con,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                             NULL);
       }
     }
 
@@ -890,8 +838,7 @@ AH_handler_policy_post (
                                                MHD_GET_ARGUMENT_KIND,
                                                "pay");
       if (NULL != order_req)
-        return begin_payment (puc,
-                              GNUNET_YES);
+        return begin_payment (puc);
     }
 
     /* Check if existing policy matches upload (and if, skip it) */
@@ -909,8 +856,10 @@ AH_handler_policy_post (
       case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
         return prepare_payment (puc);
       case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR:
-        return handle_database_error (puc,
-                                      GNUNET_DB_STATUS_HARD_ERROR);
+        return TALER_MHD_reply_with_error (puc->con,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                           NULL);
       case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS:
         /* continue below */
         break;
@@ -949,6 +898,24 @@ AH_handler_policy_post (
     return MHD_YES;
   }
 
+  if (NULL != puc->resp)
+  {
+    MHD_RESULT ret;
+
+    /* We generated a response asynchronously, queue that */
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Returning asynchronously generated response with HTTP status 
%u\n",
+                puc->response_code);
+    ret = MHD_queue_response (connection,
+                              puc->response_code,
+                              puc->resp);
+    GNUNET_break (MHD_YES == ret);
+    MHD_destroy_response (puc->resp);
+    puc->resp = NULL;
+    return ret;
+  }
+
+
   /* handle upload */
   if (0 != *recovery_data_size)
   {
@@ -973,23 +940,6 @@ AH_handler_policy_post (
     return MHD_YES;
   }
 
-  if (NULL != puc->resp)
-  {
-    MHD_RESULT ret;
-
-    /* We generated a response asynchronously, queue that */
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Returning asynchronously generated response with HTTP status 
%u\n",
-                puc->response_code);
-    ret = MHD_queue_response (connection,
-                              puc->response_code,
-                              puc->resp);
-    GNUNET_break (MHD_YES == ret);
-    MHD_destroy_response (puc->resp);
-    puc->resp = NULL;
-    return ret;
-  }
-
   /* finished with upload, check hash */
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Upload finished\n");
@@ -1037,20 +987,18 @@ AH_handler_policy_post (
       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
                                   &puc->payment_identifier,
                                   sizeof (struct ANASTASIS_PaymentSecretP));
-      return begin_payment (puc,
-                            GNUNET_YES);
+      return begin_payment (puc);
     case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED:
       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
                                   &puc->payment_identifier,
                                   sizeof (struct ANASTASIS_PaymentSecretP));
-      return begin_payment (puc,
-                            GNUNET_YES);
+      return begin_payment (puc);
     case ANASTASIS_DB_STORE_STATUS_HARD_ERROR:
-      return handle_database_error (puc,
-                                    GNUNET_DB_STATUS_HARD_ERROR);
     case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR:
-      return handle_database_error (puc,
-                                    GNUNET_DB_STATUS_SOFT_ERROR);
+      return TALER_MHD_reply_with_error (puc->con,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         NULL);
     case ANASTASIS_DB_STORE_STATUS_NO_RESULTS:
       {
         /* database says nothing actually changed, 304 (could
diff --git a/src/backend/anastasis-httpd_truth.c 
b/src/backend/anastasis-httpd_truth.c
index ce6551d..a14eb1d 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -26,7 +26,6 @@
 #include "anastasis-httpd_truth.h"
 #include <gnunet/gnunet_util_lib.h>
 #include <gnunet/gnunet_rest_lib.h>
-#include "anastasis_authorization_plugin.h"
 #include "anastasis_authorization_lib.h"
 #include <taler/taler_merchant_service.h>
 #include <taler/taler_json_lib.h>
@@ -180,6 +179,16 @@ request_done (struct TM_HandlerContext *hc)
     gc->authorization = NULL;
     gc->as = NULL;
   }
+  if (NULL != gc->cpo)
+  {
+    TALER_MERCHANT_merchant_order_get_cancel (gc->cpo);
+    gc->cpo = NULL;
+  }
+  if (NULL != gc->po)
+  {
+    TALER_MERCHANT_orders_post_cancel (gc->po);
+    gc->po = NULL;
+  }
   GNUNET_free (gc);
   hc->ctx = NULL;
 }
diff --git a/src/backend/anastasis-httpd_truth.h 
b/src/backend/anastasis-httpd_truth.h
index f523a55..7a1b95f 100644
--- a/src/backend/anastasis-httpd_truth.h
+++ b/src/backend/anastasis-httpd_truth.h
@@ -24,11 +24,24 @@
 #define ANASTASIS_HTTPD_TRUTH_H
 #include <microhttpd.h>
 
+
+/**
+ * Prepare all active GET truth requests for system shutdown.
+ */
 void
 AH_truth_shutdown (void);
 
 
 /**
+ * Prepare all active POST truth requests for system shutdown.
+ */
+void
+AH_truth_upload_shutdown (void);
+
+
+/**
+ * Handle a GET to /truth/$UUID
+ *
  * @param connection the MHD connection to handle
  * @param truth_uuid the truth UUID
  * @param con_cls
@@ -40,7 +53,10 @@ AH_handler_truth_get (
   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
   struct TM_HandlerContext *hc);
 
+
 /**
+ * Handle a POST to /truth/$UUID.
+ *
  * @param connection the MHD connection to handle
  * @param con_cls the connection's closure
  * @param truth_uuid the truth UUID
diff --git a/src/backend/anastasis-httpd_truth_upload.c 
b/src/backend/anastasis-httpd_truth_upload.c
index 8351745..b8b23cb 100644
--- a/src/backend/anastasis-httpd_truth_upload.c
+++ b/src/backend/anastasis-httpd_truth_upload.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2019 GNUnet e.V.
+  Copyright (C) 2019, 2021 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file anastasis-httpd_truth.c
- * @brief functions to handle incoming requests on /truth
+ * @file anastasis-httpd_truth_upload.c
+ * @brief functions to handle incoming POST request on /truth
  * @author Dennis Neufeld
  * @author Dominik Meister
  * @author Christian Grothoff
@@ -29,31 +29,396 @@
 #include <taler/taler_json_lib.h>
 #include <taler/taler_merchant_service.h>
 #include <taler/taler_signatures.h>
+#include "anastasis_authorization_lib.h"
 
 
+/**
+ * Information we track per truth upload.
+ */
+struct TruthUploadContext
+{
+
+  /**
+   * UUID of the truth object we are processing.
+   */
+  struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
+
+  /**
+   * Kept in DLL for shutdown handling while suspended.
+   */
+  struct TruthUploadContext *next;
+
+  /**
+   * Kept in DLL for shutdown handling while suspended.
+   */
+  struct TruthUploadContext *prev;
+
+  /**
+   * Used while we are awaiting proposal creation.
+   */
+  struct TALER_MERCHANT_PostOrdersHandle *po;
+
+  /**
+   * Used while we are waiting payment.
+   */
+  struct TALER_MERCHANT_OrderMerchantGetHandle *cpo;
+
+  /**
+   * Post parser context.
+   */
+  void *post_ctx;
+
+  /**
+   * Handle to the client request.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * HTTP response code to use on resume, if non-NULL.
+   */
+  struct MHD_Response *resp;
+
+  /**
+   * HTTP response code to use on resume, if resp is set.
+   */
+  unsigned int response_code;
+
+};
+
+
+/**
+ * Head of linked list over all truth upload processes
+ */
+static struct TruthUploadContext *tuc_head;
+
+/**
+ * Tail of linked list over all truth upload processes
+ */
+static struct TruthUploadContext *tuc_tail;
+
+
+void
+AH_truth_upload_shutdown (void)
+{
+  struct TruthUploadContext *tuc;
+
+  while (NULL != (tuc = tuc_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (tuc_head,
+                                 tuc_tail,
+                                 tuc);
+    MHD_resume_connection (tuc->connection);
+  }
+}
+
+
+/**
+ * Function called to clean up a `struct TruthUploadContext`.
+ *
+ * @param hc general handler context
+ */
+static void
+cleanup_truth_post (struct TM_HandlerContext *hc)
+{
+  struct TruthUploadContext *tuc = hc->ctx;
+
+  TALER_MHD_parse_post_cleanup_callback (tuc->post_ctx);
+  if (NULL != tuc->po)
+    TALER_MERCHANT_orders_post_cancel (tuc->po);
+  if (NULL != tuc->cpo)
+    TALER_MERCHANT_merchant_order_get_cancel (tuc->cpo);
+  if (NULL != tuc->resp)
+    MHD_destroy_response (tuc->resp);
+  GNUNET_free (tuc);
+}
+
+
+/**
+ * Transmit a payment request for @a tuc.
+ *
+ * @param tuc upload context to generate payment request for
+ */
+static void
+make_payment_request (struct TruthUploadContext *tuc)
+{
+  struct MHD_Response *resp;
+
+  /* request payment via Taler */
+  resp = MHD_create_response_from_buffer (0,
+                                          NULL,
+                                          MHD_RESPMEM_PERSISTENT);
+  GNUNET_assert (NULL != resp);
+  TALER_MHD_add_global_headers (resp);
+  {
+    char *hdr;
+    char *pfx;
+    char *hn;
+
+    if (0 == strncasecmp ("https://";,
+                          AH_backend_url,
+                          strlen ("https://";)))
+    {
+      pfx = "taler://";
+      hn = &AH_backend_url[strlen ("https://";)];
+    }
+    else if (0 == strncasecmp ("http://";,
+                               AH_backend_url,
+                               strlen ("http://";)))
+    {
+      pfx = "taler+http://";;
+      hn = &AH_backend_url[strlen ("http://";)];
+    }
+    else
+    {
+      /* This invariant holds as per check in anastasis-httpd.c */
+      GNUNET_assert (0);
+    }
+    /* This invariant holds as per check in anastasis-httpd.c */
+    GNUNET_assert (0 != strlen (hn));
+    {
+      char *order_id;
+
+      order_id = GNUNET_STRINGS_data_to_string_alloc (
+        &tuc->truth_uuid,
+        sizeof (tuc->truth_uuid));
+      GNUNET_asprintf (&hdr,
+                       "%spay/%s%s/",
+                       pfx,
+                       hn,
+                       order_id);
+      GNUNET_free (order_id);
+    }
+    GNUNET_break (MHD_YES ==
+                  MHD_add_response_header (resp,
+                                           ANASTASIS_HTTP_HEADER_TALER,
+                                           hdr));
+    GNUNET_free (hdr);
+  }
+  tuc->resp = resp;
+  tuc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
+}
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * POST /private/orders request to a merchant.
+ *
+ * @param cls our `struct TruthUploadContext`
+ * @param por response details
+ */
+static void
+proposal_cb (void *cls,
+             const struct TALER_MERCHANT_PostOrdersReply *por)
+{
+  struct TruthUploadContext *tuc = cls;
+
+  tuc->po = NULL;
+  GNUNET_CONTAINER_DLL_remove (tuc_head,
+                               tuc_tail,
+                               tuc);
+  MHD_resume_connection (tuc->connection);
+  AH_trigger_daemon (NULL);
+  if (MHD_HTTP_OK != por->hr.http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Backend returned status %u/%d\n",
+                por->hr.http_status,
+                (int) por->hr.ec);
+    GNUNET_break (0);
+    tuc->resp = TALER_MHD_make_json_pack (
+      "{s:I, s:s, s:I, s:I, s:O?}",
+      "code",
+      (json_int_t) TALER_EC_ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR,
+      "hint",
+      "Failed to setup order with merchant backend",
+      "backend-ec",
+      (json_int_t) por->hr.ec,
+      "backend-http-status",
+      (json_int_t) por->hr.http_status,
+      "backend-reply",
+      por->hr.reply);
+    GNUNET_assert (NULL != tuc->resp);
+    tuc->response_code = MHD_HTTP_BAD_GATEWAY;
+    return;
+  }
+  make_payment_request (tuc);
+}
+
+
+/**
+ * Callback to process a GET /check-payment request
+ *
+ * @param cls our `struct PolicyUploadContext`
+ * @param hr HTTP response details
+ * @param osr order status
+ */
 static void
-cleanup_parse_post_json (struct TM_HandlerContext *hc)
+check_payment_cb (void *cls,
+                  const struct TALER_MERCHANT_HttpResponse *hr,
+                  const struct TALER_MERCHANT_OrderStatusResponse *osr)
 {
-  TALER_MHD_parse_post_cleanup_callback (hc->ctx);
+  struct TruthUploadContext *tuc = cls;
+
+  tuc->cpo = NULL;
+  switch (hr->http_status)
+  {
+  case 0:
+    /* Likely timeout, complain! */
+    tuc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
+    tuc->resp = TALER_MHD_make_error (
+      TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT,
+      NULL);
+    break;
+  case MHD_HTTP_OK:
+    switch (osr->status)
+    {
+    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);
+        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");
+          break;
+        }
+      }
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Payment confirmed, resuming upload\n");
+      break;
+    case TALER_MERCHANT_OSC_UNPAID:
+    case TALER_MERCHANT_OSC_CLAIMED:
+      make_payment_request (tuc);
+      break;
+    }
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    /* Configuration issue, complain! */
+    tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+    tuc->resp = TALER_MHD_make_json_pack (
+      "{s:I, s:s, s:I, s:I, s:O?}",
+      "code",
+      (json_int_t) TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED,
+      "hint",
+      TALER_ErrorCode_get_hint (
+        TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED),
+      "backend-ec",
+      (json_int_t) hr->ec,
+      "backend-http-status",
+      (json_int_t) hr->http_status,
+      "backend-reply",
+      hr->reply);
+    GNUNET_assert (NULL != tuc->resp);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Setup fresh order */
+    {
+      char *order_id;
+      json_t *order;
+
+      order_id = GNUNET_STRINGS_data_to_string_alloc (
+        &tuc->truth_uuid,
+        sizeof(tuc->truth_uuid));
+      order = json_pack ("{s:o, s:s, s:s, s:s}",
+                         "amount",
+                         TALER_JSON_from_amount (&AH_truth_upload_fee),
+                         "summary",
+                         "Anastasis challenge storage fee",
+                         "order_id",
+                         order_id);
+      GNUNET_free (order_id);
+      tuc->po = TALER_MERCHANT_orders_post2 (AH_ctx,
+                                             AH_backend_url,
+                                             order,
+                                             GNUNET_TIME_UNIT_ZERO,
+                                             NULL, /* no payment target */
+                                             0,
+                                             NULL, /* no inventory products */
+                                             0,
+                                             NULL, /* no uuids */
+                                             false, /* do NOT require claim 
token */
+                                             &proposal_cb,
+                                             tuc);
+      AH_trigger_curl ();
+      json_decref (order);
+      return;
+    }
+  default:
+    /* Unexpected backend response */
+    tuc->response_code = MHD_HTTP_BAD_GATEWAY;
+    tuc->resp = TALER_MHD_make_json_pack (
+      "{s:I, s:s, s:I, s:I, s:O?}",
+      "code",
+      (json_int_t) TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR,
+      "hint",
+      TALER_ErrorCode_get_hint (TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR),
+      "backend-ec",
+      (json_int_t) hr->ec,
+      "backend-http-status",
+      (json_int_t) hr->http_status,
+      "backend-reply",
+      hr->reply);
+    break;
+  }
+  GNUNET_CONTAINER_DLL_remove (tuc_head,
+                               tuc_tail,
+                               tuc);
+  MHD_resume_connection (tuc->connection);
+  AH_trigger_daemon (NULL);
 }
 
 
 /**
- * @param connection the MHD connection to handle
- * @param con_cls the connection's closure
- * @param pub_key_str base32 encoded truth public key
- * @param truth_data truth data
- * @param truth_data_size number of bytes (left) in @a truth_data
- * @return MHD result code
+ * Helper function used to ask our backend to begin processing a
+ * payment for the truth upload.  May perform asynchronous operations
+ * by suspending the connection if required.
+ *
+ * @param tuc context to begin payment for.
+ * @return MHD status code
  */
+static MHD_RESULT
+begin_payment (struct TruthUploadContext *tuc)
+{
+  char *order_id;
+
+  order_id = GNUNET_STRINGS_data_to_string_alloc (
+    &tuc->truth_uuid,
+    sizeof (tuc->truth_uuid));
+  tuc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx,
+                                                AH_backend_url,
+                                                order_id,
+                                                NULL /* our payments are NOT 
session-bound */,
+                                                false,
+                                                GNUNET_TIME_UNIT_SECONDS,
+                                                &check_payment_cb,
+                                                tuc);
+  GNUNET_free (order_id);
+  if (NULL == tuc->cpo)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (tuc->connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED,
+                                       "Could not check order status");
+  }
+  return MHD_YES;
+}
+
+
 int
 AH_handler_truth_post (
   struct MHD_Connection *connection,
   struct TM_HandlerContext *hc,
-  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_public_key,
+  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
   const char *truth_data,
   size_t *truth_data_size)
 {
+  struct TruthUploadContext *tuc = hc->ctx;
   json_t *json;
   MHD_RESULT ret;
   int res;
@@ -62,17 +427,102 @@ AH_handler_truth_post (
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("keyshare_data",
                                  &truth.keyshare_data),
-    GNUNET_JSON_spec_string ("method", &truth.method),
+    GNUNET_JSON_spec_string ("method",
+                             &truth.method),
     GNUNET_JSON_spec_varsize ("encrypted_truth",
                               &truth.encrypted_truth,
                               &truth.encrypted_truth_size),
-    GNUNET_JSON_spec_string ("truth_mime", &truth.truth_mime),
+    GNUNET_JSON_spec_string ("truth_mime",
+                             &truth.truth_mime),
     GNUNET_JSON_spec_end ()
   };
 
-  hc->cc = &cleanup_parse_post_json;
+  if (NULL == tuc)
+  {
+    tuc = GNUNET_new (struct TruthUploadContext);
+    tuc->connection = connection;
+    tuc->truth_uuid = *truth_uuid;
+    hc->ctx = tuc;
+    hc->cc = &cleanup_truth_post;
+
+    /* check for excessive upload */
+    {
+      const char *lens;
+      unsigned long len;
+
+      lens = MHD_lookup_connection_value (connection,
+                                          MHD_HEADER_KIND,
+                                          MHD_HTTP_HEADER_CONTENT_LENGTH);
+      if ( (NULL == lens) ||
+           (1 != sscanf (lens,
+                         "%lu",
+                         &len)) )
+      {
+        GNUNET_break_op (0);
+        return TALER_MHD_reply_with_error (
+          connection,
+          MHD_HTTP_BAD_REQUEST,
+          (NULL == lens)
+          ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH
+          : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH,
+          NULL);
+      }
+      if (len / 1024 / 1024 >= AH_upload_limit_mb)
+      {
+        GNUNET_break_op (0);
+        return TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_PAYLOAD_TOO_LARGE,
+                                           
TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH,
+                                           "Content-length value not 
acceptable");
+      }
+    }
+
+    {
+      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) )
+          return begin_payment (tuc);
+      }
+    }
+  } /* end 'if (NULL == tuc)' */
+
+  if (NULL != tuc->resp)
+  {
+    MHD_RESULT ret;
+
+    /* We generated a response asynchronously, queue that */
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Returning asynchronously generated response with HTTP status 
%u\n",
+                tuc->response_code);
+    ret = MHD_queue_response (connection,
+                              tuc->response_code,
+                              tuc->resp);
+    GNUNET_break (MHD_YES == ret);
+    MHD_destroy_response (tuc->resp);
+    tuc->resp = NULL;
+    return ret;
+  }
+
   res = TALER_MHD_parse_post_json (connection,
-                                   &hc->ctx,
+                                   &tuc->post_ctx,
                                    truth_data,
                                    truth_data_size,
                                    &json);
@@ -84,13 +534,31 @@ AH_handler_truth_post (
   res = TALER_MHD_parse_json_data (connection,
                                    json,
                                    spec);
-
   if (GNUNET_SYSERR == res)
     return MHD_NO;   /* hard failure */
   if (GNUNET_NO == res)
     return MHD_YES;   /* failure */
+
+  /* check method is supported */
+  {
+    struct TALER_Amount dummy;
+
+    if ( (0 != strcmp ("question",
+                       truth.method)) &&
+         (NULL ==
+          ANASTASIS_authorization_plugin_load (truth.method,
+                                               AH_cfg,
+                                               &dummy)) )
+    {
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED,
+                                         truth.method);
+    }
+  }
+
   qs = db->store_truth (db->cls,
-                        truth_public_key,
+                        truth_uuid,
                         &truth.keyshare_data,
                         truth.truth_mime,
                         truth.encrypted_truth,
@@ -99,24 +567,35 @@ AH_handler_truth_post (
                         AH_truth_expiration);
   json_decref (json);
   GNUNET_JSON_parse_free (spec);
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  switch (qs)
   {
-    struct MHD_Response *resp;
+  case GNUNET_DB_STATUS_HARD_ERROR:
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+                                       "store_truth");
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_CONFLICT,
+                                       
TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS,
+                                       NULL);
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    {
+      struct MHD_Response *resp;
 
-    resp = MHD_create_response_from_buffer (0,
-                                            NULL,
-                                            MHD_RESPMEM_PERSISTENT);
-    TALER_MHD_add_global_headers (resp);
-    ret = MHD_queue_response (connection,
-                              MHD_HTTP_NO_CONTENT,
-                              resp);
-    MHD_destroy_response (resp);
-    return ret;
+      resp = MHD_create_response_from_buffer (0,
+                                              NULL,
+                                              MHD_RESPMEM_PERSISTENT);
+      TALER_MHD_add_global_headers (resp);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_NO_CONTENT,
+                                resp);
+      MHD_destroy_response (resp);
+      return ret;
+    }
   }
   GNUNET_break (0);
-  // FIXME: probably should consider 'qs' values in more detail here...
-  return TALER_MHD_reply_with_error (connection,
-                                     MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                     TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
-                                     "database failure");
+  return MHD_NO;
 }
diff --git a/src/include/anastasis.h b/src/include/anastasis.h
index cd9b0aa..93a6921 100644
--- a/src/include/anastasis.h
+++ b/src/include/anastasis.h
@@ -38,178 +38,6 @@
 struct ANASTASIS_Challenge;
 
 
-/**
- * Defines a Decryption Policy with multiple escrow methods
-*/
-struct ANASTASIS_DecryptionPolicy
-{
-  /**
-   * Set of truths identfied by UUID
-   */
-  struct ANASTASIS_CRYPTO_TruthUUIDP *uuids;
-
-  /**
-   * length of the @a uuids in this policy
-   */
-  uint32_t uuids_length;
-
-  /**
-   * encrypted masterkey ( encrypted with the policy key)
-   */
-  struct ANASTASIS_CRYPTO_EncryptedMasterKeyP emk;
-
-  /**
-   * salt used to decrypt master key
-   */
-  struct ANASTASIS_CRYPTO_SaltP salt;
-};
-
-
-/**
- * Defines the recovery information (possible policies and version of the 
recovery document)
- */
-struct ANASTASIS_RecoveryInformation
-{
-  // FIXME: document
-  struct ANASTASIS_CRYPTO_SaltP salt;
-
-  // FIXME: document
-  struct ANASTASIS_DecryptionPolicy *dps;
-
-  // FIXME: document
-  struct ANASTASIS_Challenge **cs;
-
-  // FIXME: document
-  unsigned int dps_len;
-
-  // FIXME: document
-  unsigned int cs_len;
-
-  /**
-   * Actual version obtained. FIXME: of what?
-   */
-  unsigned int version;
-};
-
-
-/**
- * Possible outcomes of a recovery process.
- */
-enum ANASTASIS_RecoveryStatus
-{
-
-  /**
-   * Recovery succeeded.
-   */
-  ANASTASIS_RS_SUCCESS = 0,
-
-  /**
-   * The HTTP download of the policy failed.
-   */
-  ANASTASIS_RS_POLICY_DOWNLOAD_FAILED,
-
-  /**
-   * We did not get a valid policy document.
-   */
-  ANASTASIS_RS_POLICY_DOWNLOAD_NO_POLICY,
-
-  /**
-   * The decompressed policy document was too big for available memory.
-   */
-  ANASTASIS_RS_POLICY_DOWNLOAD_TOO_BIG,
-
-  /**
-   * The decrypted policy document was not compressed.
-   */
-  ANASTASIS_RS_POLICY_DOWNLOAD_INVALID_COMPRESSION,
-
-  /**
-   * The decompressed policy document was not in JSON.
-   */
-  ANASTASIS_RS_POLICY_DOWNLOAD_NO_JSON,
-
-  /**
-   * The decompressed policy document was in malformed JSON.
-   */
-  ANASTASIS_RS_POLICY_MALFORMED_JSON,
-};
-
-
-/**
- * This function is called whenever the recovery process ends.
- * On success, the secret is returned in @a secret.
- *
- * @param cls handle for the callback
- * @param ec error code
- * @param secret contains the core secret which is passed to the user
- * @param secret_size defines the size of the core secret
- */
-typedef void
-(*ANASTASIS_CoreSecretCallback)(void *cls,
-                                enum ANASTASIS_RecoveryStatus rc,
-                                const void *secret,
-                                size_t secret_size);
-
-/**
- * The answer feedback defines the callback for an escrow challenge e.g. 
(wrong SMS Pin)
- *
- * @param af_cls handle for the callback
- * @param http_status_code status code
- * @param ec enum with the different possible states like wrong pin, success
- */
-typedef void
-(*ANASTASIS_AnswerFeedback)(void *af_cls,
-                            unsigned int http_status_code,
-                            enum TALER_ErrorCode ec);
-
-
-/**
- * Handle to a challenge answer operation.
- */
-struct ANASTASIS_ChallengeAnswerOperation;
-
-/**
- * Cancel a challenge answer operation.
- *
- * @param c the challenge for which to cancel the answer operation
- */
-void
-ANASTASIS_challenge_answer_cancel (
-  struct ANASTASIS_Challenge *c);
-
-
-/**
- * Challenge answer from the user like input SMS pin. Is referenced to a 
challenge and
- * sends back an AnswerFeedback.
- *
- * @param c reference to the challenge which is answered
- * @param payment_secret information about payment made for the recovery
- * @param answer user input instruction defines which input is needed
- * @param af reference to the answerfeedback which is passed back to the user
- * @param af_cls handle for the challenge answer struct
- * @return #GNUNET_OK on success
- */
-int
-ANASTASIS_challenge_answer (
-  struct ANASTASIS_Challenge *c,
-  const struct ANASTASIS_PaymentSecretP *payment_secret,
-  const char *answer,
-  ANASTASIS_AnswerFeedback af,
-  void *af_cls);
-
-
-/**
- * Defines a Challenge Callback which is initially sent with the challenge 
run. It gives back the previously
- * defined Challenge Information and a Status Code, like "payment missing".
- *
- * @param cls handle for the callback
- * @param ec enum which defines the different status codes
- *
-*/
-typedef void
-(*ANASTASIS_ChallengeCallback)(void *cls,
-                               enum TALER_ErrorCode ec); // i.e. payment 
missing
-
 /**
  * Defines the instructions for a challenge, what does the user have
  * to do to fulfill the challenge.  Also defines the method and other
@@ -232,21 +60,22 @@ struct ANASTASIS_ChallengeInformation
   /**
    * Which method is this challenge (E-Mail, Security Question, SMS...)
    */
-  char *method;
+  const char *method;
 
   /**
    * Instructions for solving the challenge (generic, set client-side
    * when challenge was established).
    */
-  char *instructions;
+  const char *instructions;
 
   /**
    * Defines the url or mail address used for the challenge. Can be NULL.
+   * // FIXME: is this really available? Where does this come from? is it an 
URL!?
    */
-  char *url;
+  // const char *url;
 
   /**
-   * true if solved, else false.
+   * true if challenged was already solved, else false.
    */
   bool solved;
 
@@ -254,13 +83,13 @@ struct ANASTASIS_ChallengeInformation
 
 
 /**
- * Returns information of a challenge.
+ * Returns detailed information about a challenge.
  *
  * @param challenge reference to the escrow challenge which is started
  * @return ANASTASIS_ChallengeInformation object
  */
 const struct ANASTASIS_ChallengeInformation *
-ANASTASIS_get_challenge (struct ANASTASIS_Challenge *challenge);
+ANASTASIS_challenge_get_details (struct ANASTASIS_Challenge *challenge);
 
 
 /**
@@ -268,8 +97,16 @@ ANASTASIS_get_challenge (struct ANASTASIS_Challenge 
*challenge);
  */
 enum ANASTASIS_ChallengeStatus
 {
+
   /**
-   * Instructions for how to solve the challenge are provided.
+   * The challenge has been solved.
+   */
+  ANASTASIS_CHALLENGE_STATUS_SOLVED,
+
+  /**
+   * Instructions for how to solve the challenge are provided.  Also
+   * used if the answer we provided was wrong (or if no answer was
+   * provided, but one is needed).
    */
   ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS,
 
@@ -281,7 +118,8 @@ enum ANASTASIS_ChallengeStatus
   /**
    * We encountered an error talking to the Anastasis service.
    */
-  ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE
+  ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE,
+
 };
 
 
@@ -290,19 +128,27 @@ enum ANASTASIS_ChallengeStatus
  */
 struct ANASTASIS_ChallengeStartResponse
 {
+  /**
+   * What is our status on satisfying this challenge. Determines @e details.
+   */
   enum ANASTASIS_ChallengeStatus cs;
 
+  /**
+   * Which challenge is this about?
+   */
+  const struct ANASTASIS_Challenge *challenge;
+
   union
   {
     /**
      * Response with server-side instructions for the user, if
-     * @e cs is ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS.
+     * @e cs is #ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS.
      */
     const char *instructions;
 
     /**
      * Response with instructions for how to pay, if
-     * @a cs is ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED.
+     * @e cs is #ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED.
      */
     struct
     {
@@ -319,6 +165,11 @@ struct ANASTASIS_ChallengeStartResponse
 
     } payment_required;
 
+
+    /**
+     * Response with details about a server-side failure, if
+     * @e cs is #ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE.
+     */
     struct
     {
 
@@ -332,6 +183,11 @@ struct ANASTASIS_ChallengeStartResponse
        */
       unsigned int http_status;
 
+      /**
+       * Taler-specific error code.
+       */
+      enum TALER_ErrorCode ec;
+
     } server_failure;
 
   } details;
@@ -346,9 +202,9 @@ struct ANASTASIS_ChallengeStartResponse
  * @param csr response details
  */
 typedef void
-(*ANASTASIS_ChallengeStartCallback)(void *cls,
-                                    const struct
-                                    ANASTASIS_ChallengeStartResponse *csr);
+(*ANASTASIS_AnswerFeedback)(
+  void *cls,
+  const struct ANASTASIS_ChallengeStartResponse *csr);
 
 
 /**
@@ -360,19 +216,62 @@ typedef void
  *
  * @param c reference to the escrow challenge which is started
  * @param psp payment secret, NULL if no payment was yet made
- * @param cc opens a ChallengePaymentCallback for the requested information
- * @param cc_cls handle for the request
+ * @param hashed_answer answer to the challenge, NULL if we have none yet
+ * @param af reference to the answerfeedback which is passed back to the user
+ * @param af_cls closure for @a af
  * @return #GNUNET_OK if the challenge was successfully started
  */
 int
 ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
                            const struct ANASTASIS_PaymentSecretP *psp,
-                           ANASTASIS_ChallengeStartCallback csc,
-                           void *csc_cls);
+                           const struct GNUNET_HashCode *hashed_answer,
+                           ANASTASIS_AnswerFeedback af,
+                           void *af_cls);
 
 
 /**
- * Abort challenge.
+ * Challenge answer from the user like input SMS pin. Is referenced to
+ * a challenge and sends back an AnswerFeedback.  Convenience
+ * wrapper around #ANASTASIS_challenge_start that hashes @a answer.
+ *
+ * @param c reference to the challenge which is answered
+ * @param psp information about payment made for the recovery
+ * @param answer user input instruction defines which input is needed
+ * @param af reference to the answerfeedback which is passed back to the user
+ * @param af_cls closure for @a af
+ * @return #GNUNET_OK on success
+ */
+int
+ANASTASIS_challenge_answer (struct ANASTASIS_Challenge *c,
+                            const struct ANASTASIS_PaymentSecretP *psp,
+                            const char *answer,
+                            ANASTASIS_AnswerFeedback af,
+                            void *af_cls);
+
+
+/**
+ * Challenge answer from the user like input SMS pin. Is referenced to
+ * a challenge and sends back an AnswerFeedback.  Convenience
+ * wrapper around #ANASTASIS_challenge_start that hashes @a answer.
+ * Variant for numeric answers.
+ *
+ * @param c reference to the challenge which is answered
+ * @param psp information about payment made for the recovery
+ * @param answer user input instruction defines which input is needed
+ * @param af reference to the answerfeedback which is passed back to the user
+ * @param af_cls closure for @a af
+ * @return #GNUNET_OK on success
+ */
+int
+ANASTASIS_challenge_answer2 (struct ANASTASIS_Challenge *c,
+                             const struct ANASTASIS_PaymentSecretP *psp,
+                             uint64_t answer,
+                             ANASTASIS_AnswerFeedback af,
+                             void *af_cls);
+
+
+/**
+ * Abort answering challenge.
  *
  * @param c reference to the escrow challenge which was started
  */
@@ -380,6 +279,70 @@ void
 ANASTASIS_challenge_abort (struct ANASTASIS_Challenge *c);
 
 
+/**
+ * Defines a Decryption Policy with multiple escrow methods
+ */
+struct ANASTASIS_DecryptionPolicy
+{
+  /**
+   * Set of truths identfied by UUID
+   */
+  struct ANASTASIS_CRYPTO_TruthUUIDP *uuids;
+
+  /**
+   * length of the @a uuids in this policy
+   */
+  uint32_t uuids_length;
+
+  /**
+   * Encrypted masterkey (encrypted with the policy key).
+   * FIXME: why exposed to the client?
+   */
+  // struct ANASTASIS_CRYPTO_EncryptedMasterKeyP emk;
+
+  /**
+   * Salt used to decrypt master key.
+   * FIXME: why exposed to the client?
+   */
+  // struct ANASTASIS_CRYPTO_SaltP salt;
+};
+
+
+/**
+ * Defines the recovery information (possible policies and version of the 
recovery document)
+ */
+struct ANASTASIS_RecoveryInformation
+{
+  // FIXME: document. Why is this exposed to the client!??
+  // struct ANASTASIS_CRYPTO_SaltP salt;
+
+  /**
+   * Array of @e dps_len policies that would allow recovery of the core secret.
+   */
+  struct ANASTASIS_DecryptionPolicy *dps;
+
+  /**
+   * Array of @e cs_len challenges to be solved (for any of the policies).
+   */
+  struct ANASTASIS_Challenge **cs;
+
+  /**
+   * Length of the @e dps array.
+   */
+  unsigned int dps_len;
+
+  /**
+   * Length of the @e cs array.
+   */
+  unsigned int cs_len;
+
+  /**
+   * Actual recovery document version obtained.
+   */
+  unsigned int version;
+};
+
+
 /**
  * Callback which passes back the recovery document and its possible
  * policies. Also passes back the version of the document for the user
@@ -393,6 +356,65 @@ typedef void
                             const struct ANASTASIS_RecoveryInformation *ri);
 
 
+/**
+ * Possible outcomes of a recovery process.
+ */
+enum ANASTASIS_RecoveryStatus
+{
+
+  /**
+   * Recovery succeeded.
+   */
+  ANASTASIS_RS_SUCCESS = 0,
+
+  /**
+   * The HTTP download of the policy failed.
+   */
+  ANASTASIS_RS_POLICY_DOWNLOAD_FAILED,
+
+  /**
+   * We did not get a valid policy document.
+   */
+  ANASTASIS_RS_POLICY_DOWNLOAD_NO_POLICY,
+
+  /**
+   * The decompressed policy document was too big for available memory.
+   */
+  ANASTASIS_RS_POLICY_DOWNLOAD_TOO_BIG,
+
+  /**
+   * The decrypted policy document was not compressed.
+   */
+  ANASTASIS_RS_POLICY_DOWNLOAD_INVALID_COMPRESSION,
+
+  /**
+   * The decompressed policy document was not in JSON.
+   */
+  ANASTASIS_RS_POLICY_DOWNLOAD_NO_JSON,
+
+  /**
+   * The decompressed policy document was in malformed JSON.
+   */
+  ANASTASIS_RS_POLICY_MALFORMED_JSON,
+};
+
+
+/**
+ * This function is called whenever the recovery process ends.
+ * On success, the secret is returned in @a secret.
+ *
+ * @param cls handle for the callback
+ * @param ec error code
+ * @param secret contains the core secret which is passed to the user
+ * @param secret_size defines the size of the core secret
+ */
+typedef void
+(*ANASTASIS_CoreSecretCallback)(void *cls,
+                                enum ANASTASIS_RecoveryStatus rc,
+                                const void *secret,
+                                size_t secret_size);
+
+
 /**
  * stores provider URIs, identity key material, decrypted recovery document 
(internally!)
  */
@@ -435,13 +457,6 @@ ANASTASIS_recovery_abort (struct ANASTASIS_Recovery *r);
 /* ************************* Backup API ***************************** */
 
 
-/**
- * Handle for the operation to establish a truth object by sharing
- * an encrypted key share with an Anastasis provider.
- */
-struct ANASTASIS_TruthUpload;
-
-
 /**
  * Represents a truth object, which is a key share and the respective
  * challenge to be solved with an Anastasis provider to recover the
@@ -450,6 +465,38 @@ struct ANASTASIS_TruthUpload;
 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * Handle for the operation to establish a truth object by sharing
+ * an encrypted key share with an Anastasis provider.
+ */
+struct ANASTASIS_TruthUpload;
+
+
 /**
  * Upload result information.  The resulting truth object can be used
  * to create policies.  If payment is required, the @a taler_pay_url
@@ -570,8 +617,19 @@ struct ANASTASIS_SharePaymentRequest
  */
 enum ANASTASIS_ShareStatus
 {
+  /**
+   * Upload successful.
+   */
   ANASTASIS_SHARE_STATUS_SUCCESS = 0,
+
+  /**
+   * Upload requires payment.
+   */
   ANASTASIS_SHARE_STATUS_PAYMENT_REQUIRED,
+
+  /**
+   * Failure to upload secret share at the provider.
+   */
   ANASTASIS_SHARE_STATUS_PROVIDER_FAILED
 };
 
diff --git a/src/include/anastasis_database_plugin.h 
b/src/include/anastasis_database_plugin.h
index 50d8eb6..ab29c78 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -500,6 +500,38 @@ struct ANASTASIS_DatabasePlugin
     const struct TALER_Amount *amount);
 
 
+  /**
+   * Record truth upload payment was made.
+   *
+   * @param cls closure
+   * @param uuid the truth's UUID
+   * @param amount the amount that was paid
+   * @param duration how long is the truth paid for
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*record_truth_upload_payment)(
+    void *cls,
+    const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
+    const struct TALER_Amount *amount,
+    struct GNUNET_TIME_Relative duration);
+
+
+  /**
+   * Inquire whether truth upload payment was made.
+   *
+   * @param cls closure
+   * @param uuid the truth's UUID
+   * @param[out] paid_until set for how long this truth is paid for
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*check_truth_upload_paid)(
+    void *cls,
+    const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
+    struct GNUNET_TIME_Absolute *paid_until);
+
+
   /**
    * Verify the provided code with the code on the server.
    * If the code matches the function will return with success, if the code
diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h
index 35f526f..173eb1f 100644
--- a/src/include/anastasis_service.h
+++ b/src/include/anastasis_service.h
@@ -287,30 +287,8 @@ struct ANASTASIS_UploadDetails
     const struct GNUNET_HashCode *curr_backup_hash;
 
     /**
-     * Previous backup. Returned if @e us is
-     * #ANASTASIS_US_CONFLICTING_POLICY
+     * Details about required payment.
      */
-    struct
-    {
-      /**
-       * Hash over @e existing_backup.
-       */
-      struct GNUNET_HashCode existing_backup_hash;
-
-      /**
-       * Number of bytes in @e existing_backup.
-       */
-      size_t existing_backup_size;
-
-      /**
-       * The backup on the server, which does not match the
-       * "previous" backup expected by the client and thus
-       * needs to be decrypted, reconciled and re-uploaded.
-       */
-      const void *existing_backup;
-
-    } recovered_backup;
-
     struct
     {
       /**
@@ -342,7 +320,7 @@ typedef void
 
 
 /**
- * Store policies, does a POST /policy/$AccountPub
+ * Store policies, does a POST /policy/$ACCOUNT_PUB
  *
  * @param ctx the CURL context used to connect to the backend
  * @param backend_url backend's base URL, including final "/"
@@ -381,20 +359,146 @@ ANASTASIS_policy_store_cancel (
 /****** TRUTH API ******/
 
 
+/**
+ * Operational status.
+ */
+enum ANASTASIS_KeyShareDownloadStatus
+{
+  /**
+   * We got the encrypted key share.
+   */
+  ANASTASIS_KSD_SUCCESS = 0,
+
+  /**
+   * Payment is needed to proceed with the recovery.
+   */
+  ANASTASIS_KSD_PAYMENT_REQUIRED,
+
+  /**
+   * The provided answer was wrong or missing. Instructions for
+   * getting a good answer may be provided.
+   */
+  ANASTASIS_KSD_INVALID_ANSWER,
+
+  /**
+   * To answer the challenge, the client should be redirected to
+   * the given URL.
+   */
+  ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION,
+
+  /**
+   * The provider had an error.
+   */
+  ANASTASIS_KSD_SERVER_ERROR,
+
+  /**
+   * The provider claims we made an error.
+   */
+  ANASTASIS_KSD_CLIENT_FAILURE,
+
+  /**
+   * The provider does not know this truth.
+   */
+  ANASTASIS_KSD_TRUTH_UNKNOWN
+
+};
+
+
 /**
  * Detailed results from the successful download.
  */
 struct ANASTASIS_KeyShareDownloadDetails
 {
+
   /**
-   * The backup we downloaded
+   * Operational status.
    */
-  const void *encrypted_key_share;
+  enum ANASTASIS_KeyShareDownloadStatus status;
 
   /**
-   * Number of bytes in @e encrypted_key_share.
+   * Anastasis URL that returned the @e status.
    */
-  size_t encrypted_keyshare_size;
+  const char *server_url;
+
+  /**
+   * Details depending on @e status.
+   */
+  union
+  {
+
+    /**
+     * The encrypted key share (if @e status is #ANASTASIS_KSD_SUCCESS).
+     */
+    struct ANASTASIS_CRYPTO_EncryptedKeyShareP eks;
+
+    /**
+     * Response if the challenge sitll needs to be answered, and the
+     * instructions are provided inline (no redirection).
+     */
+    struct
+    {
+
+      /**
+       * HTTP status returned by the server.  #MHD_HTTP_ALREADY_REPORTED
+       * if the server did already send the challenge to the user,
+       * #MHD_HTTP_FORBIDDEN if the answer was wrong (or missing).
+       */
+      unsigned int http_status;
+
+      /**
+       * Response with server-side instructions for the user
+       */
+      const char *instructions;
+
+    } open_challenge;
+
+    /**
+     * URL with instructions for the user to satisfy the challenge, if
+     * @e status is #ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION.
+     */
+    const char *redirect_url;
+
+    /**
+     * Response with instructions for how to pay, if
+     * @e status is #ANASTASIS_KSD_PAYMENT_REQUIRED.
+     */
+    struct
+    {
+
+      /**
+       * "taler://pay" URL with details how to pay for the challenge.
+       */
+      const char *taler_pay_uri;
+
+      /**
+       * The order ID from @e taler_pay_uri.
+       */
+      struct ANASTASIS_PaymentSecretP payment_secret;
+
+    } payment_required;
+
+
+    /**
+     * Response with details about a server-side failure, if
+     * @e status is #ANASTASIS_KSD_SERVER_FAILURE,
+     * #ANASTASIS_KSD_CLIENT_FAILURE or #ANASTASIS_KSD_TRUTH_UNKNOWN.
+     */
+    struct
+    {
+
+      /**
+       * HTTP status returned by the server.
+       */
+      unsigned int http_status;
+
+      /**
+       * Taler-specific error code.
+       */
+      enum TALER_ErrorCode ec;
+
+    } server_failure;
+
+  } details;
 };
 
 
@@ -409,13 +513,11 @@ struct ANASTASIS_KeyShareLookupOperation;
  *
  * @param cls closure
  * @param http_status HTTP status code for this request
- * @param ec anastasis-specific error code
- * @param obj the response body
+ * @param kdd details about the key share
  */
 typedef void
 (*ANASTASIS_KeyShareLookupCallback) (
   void *cls,
-  unsigned int http_status,
   const struct ANASTASIS_KeyShareDownloadDetails *kdd);
 
 
@@ -436,7 +538,7 @@ struct ANASTASIS_KeyShareLookupOperation *
 ANASTASIS_keyshare_lookup (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_public_key,
+  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
   const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key,
   const struct ANASTASIS_PaymentSecretP *payment_secret,
   const struct GNUNET_HashCode *hashed_answer,
@@ -472,7 +574,7 @@ typedef void
 
 
 /**
- * Store Truth, does a POST /truth/$TRUTH_PUBLIC_KEY
+ * Store Truth, does a POST /truth/$UUID
  *
  * @param ctx the CURL context used to connect to the backend
  * @param backend_url backend's base URL, including final "/"
diff --git a/src/lib/anastasis_recovery.c b/src/lib/anastasis_recovery.c
index 4decaf3..7699a05 100644
--- a/src/lib/anastasis_recovery.c
+++ b/src/lib/anastasis_recovery.c
@@ -161,7 +161,7 @@ struct ANASTASIS_Challenge
   /**
    * The /truth GET operation handle.
    */
-  struct ANASTASIS_ChallengeRunOperation *cro;
+  struct ANASTASIS_KeyShareLookupOperation *cro;
 
   /**
    * Callback which gives back the instructions and a status code of
@@ -203,6 +203,41 @@ keyshare_lookup_cb (void *cls,
            http_status,
            TALER_EC_NONE);
     break;
+  case MHD_HTTP_PAYMENT_REQUIRED:
+    {
+      struct ANASTASIS_ChallengeStartResponse csr = {
+        .cs = ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED,
+        .details.payment_required.server_url = c->ci.url,
+        .details.payment_required.taler_pay_url = response_string
+      };
+
+      c->csc (c->csc_cls,
+              &csr);
+      break;
+    }
+  case MHD_HTTP_OK:
+    {
+      struct ANASTASIS_ChallengeStartResponse csr = {
+        .cs = ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS,
+        .details.instructions = response_string
+      };
+
+      c->csc (c->csc_cls,
+              &csr);
+      break;
+    }
+  default:
+    {
+      struct ANASTASIS_ChallengeStartResponse csr = {
+        .cs = ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE,
+        .details.server_failure.message = response_string,
+        .details.server_failure.http_status = http_status
+      };
+
+      c->csc (c->csc_cls,
+              &csr);
+      break;
+    }
   default:
     c->af (c->af_cls,
            http_status,
@@ -287,29 +322,20 @@ keyshare_lookup_cb (void *cls,
 }
 
 
-void
-ANASTASIS_challenge_answer_cancel (struct ANASTASIS_Challenge *c)
+const struct ANASTASIS_ChallengeInformation *
+ANASTASIS_get_challenge (struct ANASTASIS_Challenge *challenge)
 {
-  if (NULL == c->kslo)
-  {
-    GNUNET_break (0);
-    return;
-  }
-  ANASTASIS_keyshare_lookup_cancel (c->kslo);
-  c->kslo = NULL;
+  return &challenge->ci;
 }
 
 
 int
-ANASTASIS_challenge_answer (
-  struct ANASTASIS_Challenge *c,
-  const struct ANASTASIS_PaymentSecretP *payment_secret,
-  const char *answer_str,
-  ANASTASIS_AnswerFeedback af,
-  void *af_cls)
+ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
+                           const struct ANASTASIS_PaymentSecretP *psp,
+                           const struct GNUNET_HashCode *hashed_answer,
+                           ANASTASIS_AnswerFeedback af,
+                           void *af_cls)
 {
-  struct GNUNET_HashCode hashed_answer;
-
   if (c->ci.solved)
   {
     GNUNET_break (0);
@@ -317,15 +343,12 @@ ANASTASIS_challenge_answer (
   }
   c->af = af;
   c->af_cls = af_cls;
-  GNUNET_CRYPTO_hash (answer_str,
-                      strlen (answer_str),
-                      &hashed_answer);
   c->kslo = ANASTASIS_keyshare_lookup (c->recovery->ctx,
                                        c->ci.url,
                                        &c->uuid,
                                        &c->truth_key,
-                                       payment_secret,
-                                       &hashed_answer,
+                                       psp,
+                                       hashed_answer,
                                        &keyshare_lookup_cb,
                                        c);
   if (NULL == c->kslo)
@@ -337,92 +360,45 @@ ANASTASIS_challenge_answer (
 }
 
 
-const struct ANASTASIS_ChallengeInformation *
-ANASTASIS_get_challenge (struct ANASTASIS_Challenge *challenge)
-{
-  return &challenge->ci;
-}
-
-
-/**
- * Function called with the results of a #ANASTASIS_challenge_run().
- *
- * @param cls closure
- * @param http_status HTTP status of the request
- * @param response_string payment request or instructions
- */
-static void
-challenge_run_cb (void *cls,
-                  unsigned int http_status,
-                  char *response_string)
+int
+ANASTASIS_challenge_answer (
+  struct ANASTASIS_Challenge *c,
+  const struct ANASTASIS_PaymentSecretP *psp,
+  const char *answer_str,
+  ANASTASIS_AnswerFeedback af,
+  void *af_cls)
 {
-  struct ANASTASIS_Challenge *c = cls;
-
-  c->cro = NULL;
-  switch (http_status)
-  {
-  case MHD_HTTP_PAYMENT_REQUIRED:
-    {
-      struct ANASTASIS_ChallengeStartResponse csr = {
-        .cs = ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED,
-        .details.payment_required.server_url = c->ci.url,
-        .details.payment_required.taler_pay_url = response_string
-      };
-
-      c->csc (c->csc_cls,
-              &csr);
-      break;
-    }
-  case MHD_HTTP_OK:
-    {
-      struct ANASTASIS_ChallengeStartResponse csr = {
-        .cs = ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS,
-        .details.instructions = response_string
-      };
-
-      c->csc (c->csc_cls,
-              &csr);
-      break;
-    }
-  default:
-    {
-      struct ANASTASIS_ChallengeStartResponse csr = {
-        .cs = ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE,
-        .details.server_failure.message = response_string,
-        .details.server_failure.http_status = http_status
-      };
+  struct GNUNET_HashCode hashed_answer;
 
-      c->csc (c->csc_cls,
-              &csr);
-      break;
-    }
-  }
-  c->csc = NULL;
-  c->csc_cls = NULL;
+  GNUNET_CRYPTO_hash (answer_str,
+                      strlen (answer_str),
+                      &hashed_answer);
+  return ANASTASIS_challenge_start (c,
+                                    psp,
+                                    &hashed_answer,
+                                    af,
+                                    af_cls);
 }
 
 
 int
-ANASTASIS_challenge_start (struct ANASTASIS_Challenge *c,
-                           const struct ANASTASIS_PaymentSecretP *psp,
-                           ANASTASIS_ChallengeStartCallback csc,
-                           void *csc_cls)
+ANASTASIS_challenge_answer2 (struct ANASTASIS_Challenge *c,
+                             const struct ANASTASIS_PaymentSecretP *psp,
+                             uint64_t answer,
+                             ANASTASIS_AnswerFeedback af,
+                             void *af_cls)
 {
-  c->csc = csc;
-  c->csc_cls = csc_cls;
-  c->cro = ANASTASIS_challenge_run (c->recovery->ctx,
-                                    c->ci.url,
-                                    &c->uuid,
-                                    &c->truth_key,
-                                    psp,
-                                    &challenge_run_cb,
-                                    c);
-  if (NULL == c->cro)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
+  char answer_s[40];
+
+  GNUNET_snprintf (answer_s,
+                   sizeof (answer_s),
+                   "%llu",
+                   (unsigned long long) answer);
+  return ANASTASIS_challenge_answer2 (c,
+                                      psp,
+                                      answer_s,
+                                      af,
+                                      af_cls);
 }
 
 
@@ -439,8 +415,8 @@ ANASTASIS_challenge_abort (struct ANASTASIS_Challenge *c)
     GNUNET_break (0);
     return;
   }
-  ANASTASIS_challenge_run_cancel (c->cro);
-  c->cro = NULL;
+  ANASTASIS_keyshare_lookup_cancel (c->kslo);
+  c->kslo = NULL;
   c->csc = NULL;
   c->csc_cls = NULL;
 }
@@ -775,6 +751,8 @@ ANASTASIS_recovery_begin (struct GNUNET_CURL_Context *ctx,
 }
 
 
+]
+
 void
 ANASTASIS_recovery_abort (struct ANASTASIS_Recovery *r)
 {
diff --git a/src/reducer/anastasis_api_backup_redux.c 
b/src/reducer/anastasis_api_backup_redux.c
index a67c8f2..417b94a 100644
--- a/src/reducer/anastasis_api_backup_redux.c
+++ b/src/reducer/anastasis_api_backup_redux.c
@@ -1347,6 +1347,7 @@ initialize_policies (json_t *state,
         json_t *json_truth
           = json_array_get (json_object_get (state, "truths"),
                             atoi (tu_key));
+#if FIXME_LOGIC
         if (json_integer_value (
               json_object_get (json_truth, "auth_method_index"))
             == auth_method_index)
@@ -1357,6 +1358,7 @@ initialize_policies (json_t *state,
           truth_index++;
           break;
         }
+#endif
       }
       {
         // initialize recovery document uploads array
@@ -1703,7 +1705,7 @@ truth_upload_cb (void *cls,
                    "status",
                    (json_int_t) MHD_HTTP_PAYMENT_REQUIRED,
                    "pay_url",
-                   ud->details.payment_request);
+                   ud->details.payment.payment_request);
     GNUNET_assert (NULL != p);
     {
       GNUNET_snprintf (buf,
@@ -1728,6 +1730,7 @@ truth_upload_cb (void *cls,
   tus->truth = t;
   truth_uploads = json_object_get (tus->state,
                                    "truth_uploads");
+#if FIXME_LOGIC
   j = ANASTASIS_truth_to_json (tus->truth);
   GNUNET_assert (NULL != j);
   p = json_pack ("{s:I,s:o}",
@@ -1735,6 +1738,7 @@ truth_upload_cb (void *cls,
                  (json_int_t) MHD_HTTP_NO_CONTENT,
                  "truth",
                  j);
+#endif
   GNUNET_assert (NULL != p);
   {
     char buf[13];
diff --git a/src/reducer/anastasis_api_redux.c 
b/src/reducer/anastasis_api_redux.c
index 6c9547b..2807979 100644
--- a/src/reducer/anastasis_api_redux.c
+++ b/src/reducer/anastasis_api_redux.c
@@ -928,9 +928,9 @@ policy_lookup_cb (void *cls,
     bool contains = false;
     for (unsigned int i = 0; i < rss->challenges_length; i++)
     {
-      if (0 == memcmp (&rss->challenges[i].nonce,
-                       &ci->nonce,
-                       sizeof(struct ANASTASIS_CRYPTO_NonceP)))
+      if (0 ==
+          GNUNET_memcmp (&rss->challenges[i].uuid,
+                         &ci->uuid))
       {
         contains = true;
         break;
@@ -942,7 +942,7 @@ policy_lookup_cb (void *cls,
                            rss->challenges_length,
                            *ci);
 #if FIXME
-      /* should probably only serialize the nonce and
+      /* should probably only serialize the uuid and
          what the UI needs! */
       GNUNET_assert (
         0 == json_array_append_new (
@@ -955,19 +955,19 @@ policy_lookup_cb (void *cls,
   {
     json_t *policy = json_array ();
     GNUNET_assert (json_is_array (policy));
-    for (unsigned int j = 0; j < ri->dps[i].nonces_length; j++)
+    for (unsigned int j = 0; j < ri->dps[i].uuids_length; j++)
     {
       for (unsigned int k = 0; k < rss->challenges_length; k++)
       {
-        if (0 == memcmp (&ri->dps[i].nonces[j],
-                         &rss->challenges[k].nonce,
-                         sizeof (struct ANASTASIS_CRYPTO_NonceP)))
+        if (0 ==
+            GNUNET_memcmp (&ri->dps[i].uuids[j],
+                           &rss->challenges[k].uuid))
           GNUNET_assert (
             0 == json_array_append_new (policy,
                                         json_integer ((json_int_t) k)));
       }
     }
-    GNUNET_free (ri->dps[i].nonces);
+    GNUNET_free (ri->dps[i].uuids);
     if (0 < json_array_size (policy))
       GNUNET_assert (
         0 == json_array_append_new (policies,
diff --git a/src/restclient/anastasis_api_keyshare_lookup.c 
b/src/restclient/anastasis_api_keyshare_lookup.c
index fd64f97..f65f6a1 100644
--- a/src/restclient/anastasis_api_keyshare_lookup.c
+++ b/src/restclient/anastasis_api_keyshare_lookup.c
@@ -26,6 +26,8 @@
 #include <microhttpd.h> /* just for HTTP status codes */
 #include "anastasis_service.h"
 #include "anastasis_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_merchant_service.h>
 
 
 /**
@@ -38,6 +40,11 @@ struct ANASTASIS_KeyShareLookupOperation
    */
   char *url;
 
+  /**
+   * The url for this request, without response parameter.
+   */
+  char *display_url;
+
   /**
    * Handle for the request.
    */
@@ -73,6 +80,15 @@ struct ANASTASIS_KeyShareLookupOperation
    */
   const struct GNUNET_HashCode *hashed_answer;
 
+  /**
+   * Payment URI we received from the service, or NULL.
+   */
+  char *pay_uri;
+
+  /**
+   * Location URI we received from the service, or NULL.
+   */
+  char *location;
 };
 
 
@@ -85,6 +101,9 @@ ANASTASIS_keyshare_lookup_cancel (
     GNUNET_CURL_job_cancel (kslo->job);
     kslo->job = NULL;
   }
+  GNUNET_free (kslo->location);
+  GNUNET_free (kslo->pay_uri);
+  GNUNET_free (kslo->display_url);
   GNUNET_free (kslo->url);
   GNUNET_free (kslo);
 }
@@ -92,6 +111,11 @@ ANASTASIS_keyshare_lookup_cancel (
 
 /**
  * Process GET /truth response
+ *
+ * @param cls our `struct ANASTASIS_KeyShareLookupOperation *`
+ * @param response_code the HTTP status
+ * @param data the body of the response
+ * @param data_size number of bytes in @a data
  */
 static void
 handle_keyshare_lookup_finished (void *cls,
@@ -100,68 +124,215 @@ handle_keyshare_lookup_finished (void *cls,
                                  size_t data_size)
 {
   struct ANASTASIS_KeyShareLookupOperation *kslo = cls;
+  struct ANASTASIS_KeyShareDownloadDetails kdd;
 
   kslo->job = NULL;
+  memset (&kdd,
+          0,
+          sizeof (kdd));
+  kdd.server_url = kslo->display_url;
   switch (response_code)
   {
   case 0:
     /* Hard error */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Backend didn't even return from GET /truth\n");
+    kdd.status = ANASTASIS_KSD_SERVER_ERROR;
     break;
-
   case MHD_HTTP_OK:
+    if (sizeof (struct ANASTASIS_CRYPTO_EncryptedKeyShareP) == data_size)
     {
-      struct ANASTASIS_KeyShareDownloadDetails kdd;
-
       /* Success, call callback with all details! */
-      memset (&kdd, 0, sizeof (kdd));
-      kdd.encrypted_key_share = data;
-      kdd.encrypted_keyshare_size = data_size;
+      memcpy (&kdd.details.eks,
+              data,
+              data_size);
       kslo->cb (kslo->cb_cls,
-                (unsigned int) response_code,
                 &kdd);
-      kslo->cb = NULL;
       ANASTASIS_keyshare_lookup_cancel (kslo);
       return;
     }
-  case MHD_HTTP_ALREADY_REPORTED:
-    /* Nothing really to verify */
-    break;
+    else
+    {
+      GNUNET_break_op (0);
+      kdd.status = ANASTASIS_KSD_SERVER_ERROR;
+      break;
+    }
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the anastasis server is buggy
        (or API version conflict); just pass JSON reply to the application */
+    GNUNET_break (0);
+    kdd.status = ANASTASIS_KSD_CLIENT_FAILURE;
     break;
-  case MHD_HTTP_FORBIDDEN:
-    /* Nothing really to verify, authentication failed */
+  case MHD_HTTP_PAYMENT_REQUIRED:
+    {
+      struct TALER_MERCHANT_PayUriData pd;
+
+      if ( (NULL == kslo->pay_uri) ||
+           (GNUNET_OK !=
+            TALER_MERCHANT_parse_pay_uri (kslo->pay_uri,
+                                          &pd)) )
+      {
+        GNUNET_break_op (0);
+        kdd.status = ANASTASIS_KSD_SERVER_ERROR;
+        break;
+      }
+      if (GNUNET_OK !=
+          GNUNET_STRINGS_string_to_data (
+            pd.order_id,
+            strlen (pd.order_id),
+            &kdd.details.payment_required.payment_secret,
+            sizeof (kdd.details.payment_required.payment_secret)))
+      {
+        GNUNET_break (0);
+        kdd.status = ANASTASIS_KSD_SERVER_ERROR;
+        break;
+      }
+      kdd.status = ANASTASIS_KSD_PAYMENT_REQUIRED;
+      kdd.details.payment_required.taler_pay_uri = kslo->pay_uri;
+      kslo->cb (kslo->cb_cls,
+                &kdd);
+      ANASTASIS_keyshare_lookup_cancel (kslo);
+      return;
+    }
     break;
+  case MHD_HTTP_SEE_OTHER:
+    /* Nothing really to verify, authentication required/failed */
+    kdd.status = ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION;
+    kdd.details.redirect_url = kslo->location;
+    kslo->cb (kslo->cb_cls,
+              &kdd);
+    ANASTASIS_keyshare_lookup_cancel (kslo);
+    return;
+  case MHD_HTTP_ALREADY_REPORTED:
+  case MHD_HTTP_FORBIDDEN:
+    /* Nothing really to verify, authentication required/failed */
+    {
+      char *instructions;
+
+      instructions = GNUNET_strndup (data,
+                                     data_size);
+      kdd.status = ANASTASIS_KSD_INVALID_ANSWER;
+      kdd.details.open_challenge.instructions = instructions;
+      kdd.details.open_challenge.http_status = response_code;
+      kslo->cb (kslo->cb_cls,
+                &kdd);
+      GNUNET_free (instructions);
+    }
+    ANASTASIS_keyshare_lookup_cancel (kslo);
+    return;
   case MHD_HTTP_NOT_FOUND:
     /* Nothing really to verify */
+    kdd.status = ANASTASIS_KSD_TRUTH_UNKNOWN;
     break;
   case MHD_HTTP_GONE:
     /* Nothing really to verify */
+    kdd.status = ANASTASIS_KSD_TRUTH_UNKNOWN;
+    break;
+  case MHD_HTTP_EXPECTATION_FAILED:
+    /* Nothing really to verify */
+    kdd.status = ANASTASIS_KSD_CLIENT_FAILURE;
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
+    kdd.status = ANASTASIS_KSD_SERVER_ERROR;
     break;
   default:
     /* unexpected response code */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
+                "Unexpected response code %u to GET /truth\n",
                 (unsigned int) response_code);
     GNUNET_break (0);
-    response_code = 0;
+    kdd.status = ANASTASIS_KSD_SERVER_ERROR;
     break;
   }
-  if (NULL != kslo->cb)
+  kdd.details.server_failure.ec = TALER_JSON_get_error_code2 (data,
+                                                              data_size);
+  kdd.details.server_failure.http_status = response_code;
+  kslo->cb (kslo->cb_cls,
+            &kdd);
+  ANASTASIS_keyshare_lookup_cancel (kslo);
+}
+
+
+/**
+ * Handle HTTP header received by curl.
+ *
+ * @param buffer one line of HTTP header data
+ * @param size size of an item
+ * @param nitems number of items passed
+ * @param userdata our `struct ANASTASIS_StorePolicyOperation *`
+ * @return `size * nitems`
+ */
+static size_t
+handle_header (char *buffer,
+               size_t size,
+               size_t nitems,
+               void *userdata)
+{
+  struct ANASTASIS_KeyShareLookupOperation *kslo = userdata;
+  size_t total = size * nitems;
+  char *ndup;
+  const char *hdr_type;
+  char *hdr_val;
+  char *sp;
+
+  ndup = GNUNET_strndup (buffer,
+                         total);
+  hdr_type = strtok_r (ndup,
+                       ":",
+                       &sp);
+  if (NULL == hdr_type)
   {
-    kslo->cb (kslo->cb_cls,
-              response_code,
-              NULL);
-    kslo->cb = NULL;
+    GNUNET_free (ndup);
+    return total;
   }
-  ANASTASIS_keyshare_lookup_cancel (kslo);
+  hdr_val = strtok_r (NULL,
+                      "",
+                      &sp);
+  if (NULL == hdr_val)
+  {
+    GNUNET_free (ndup);
+    return total;
+  }
+  if (' ' == *hdr_val)
+    hdr_val++;
+  if (0 == strcasecmp (hdr_type,
+                       ANASTASIS_HTTP_HEADER_TALER))
+  {
+    size_t len;
+
+    /* found payment URI we care about! */
+    GNUNET_free (kslo->pay_uri);
+    kslo->pay_uri = GNUNET_strdup (hdr_val);
+    len = strlen (kslo->pay_uri);
+    while ( (len > 0) &&
+            ( ('\n' == kslo->pay_uri[len - 1]) ||
+              ('\r' == kslo->pay_uri[len - 1]) ) )
+    {
+      len--;
+      kslo->pay_uri[len] = '\0';
+    }
+  }
+  if (0 == strcasecmp (hdr_type,
+                       MHD_HTTP_HEADER_LOCATION))
+  {
+    size_t len;
+
+    /* found location URI we care about! */
+    GNUNET_free (kslo->location);
+    kslo->location = GNUNET_strdup (hdr_val);
+    len = strlen (kslo->location);
+    while ( (len > 0) &&
+            ( ('\n' == kslo->location[len - 1]) ||
+              ('\r' == kslo->location[len - 1]) ) )
+    {
+      len--;
+      kslo->location[len] = '\0';
+    }
+  }
+  GNUNET_free (ndup);
+  return total;
 }
 
 
@@ -169,7 +340,7 @@ struct ANASTASIS_KeyShareLookupOperation *
 ANASTASIS_keyshare_lookup (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_public_key,
+  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
   const struct ANASTASIS_CRYPTO_TruthKeyP *truth_key,
   const struct ANASTASIS_PaymentSecretP *payment_secret,
   const struct GNUNET_HashCode *hashed_answer,
@@ -236,26 +407,45 @@ ANASTASIS_keyshare_lookup (
   kslo->ctx = ctx;
   kslo->truth_key = truth_key;
   {
-    char *pub_key_str;
+    char *uuid_str;
 
-    pub_key_str = GNUNET_STRINGS_data_to_string_alloc (truth_public_key,
-                                                       sizeof 
(*truth_public_key));
+    uuid_str = GNUNET_STRINGS_data_to_string_alloc (truth_uuid,
+                                                    sizeof (*truth_uuid));
     GNUNET_asprintf (&path,
                      "truth/%s",
-                     pub_key_str);
-    GNUNET_free (pub_key_str);
+                     uuid_str);
+    GNUNET_free (uuid_str);
+  }
+  if (NULL != hashed_answer)
+  {
+    answer_s = GNUNET_STRINGS_data_to_string_alloc (hashed_answer,
+                                                    sizeof (*hashed_answer));
+    kslo->url = TALER_url_join (backend_url,
+                                path,
+                                "response",
+                                answer_s,
+                                NULL);
+    GNUNET_free (answer_s);
+  }
+  else
+  {
+    kslo->url = TALER_url_join (backend_url,
+                                path,
+                                NULL);
   }
-  answer_s = GNUNET_STRINGS_data_to_string_alloc (hashed_answer,
-                                                  sizeof (*hashed_answer));
-
-  kslo->url = TALER_url_join (backend_url,
-                              path,
-                              "response",
-                              answer_s,
-                              NULL);
-  GNUNET_free (answer_s);
+  kslo->display_url = TALER_url_join (backend_url,
+                                      path,
+                                      NULL);
   GNUNET_free (path);
   eh = ANASTASIS_curl_easy_get_ (kslo->url);
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HEADERFUNCTION,
+                                   &handle_header));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HEADERDATA,
+                                   kslo));
   kslo->cb = cb;
   kslo->cb_cls = cb_cls;
   kslo->job = GNUNET_CURL_job_add_raw (ctx,
@@ -263,7 +453,6 @@ ANASTASIS_keyshare_lookup (
                                        job_headers,
                                        &handle_keyshare_lookup_finished,
                                        kslo);
-  GNUNET_free (path);
   curl_slist_free_all (job_headers);
   return kslo;
 }
diff --git a/src/restclient/anastasis_api_truth_store.c 
b/src/restclient/anastasis_api_truth_store.c
index 7183930..5d5723c 100644
--- a/src/restclient/anastasis_api_truth_store.c
+++ b/src/restclient/anastasis_api_truth_store.c
@@ -157,23 +157,10 @@ handle_truth_store_finished (void *cls,
     ud.details.payment.payment_request = tso->pay_uri;
     udp = &ud;
     break;
-  case MHD_HTTP_FORBIDDEN:
-    GNUNET_break (0);
-    ud.ec = TALER_JSON_get_error_code2 (data,
-                                        data_size);
-    break;
   case MHD_HTTP_CONFLICT:
     ud.us = ANASTASIS_US_CONFLICTING_TRUTH;
-    ud.details.recovered_backup.existing_backup_size
-      = data_size;
-    ud.details.recovered_backup.existing_backup
-      = data;
     udp = &ud;
     break;
-  case MHD_HTTP_GONE:
-    ud.ec = TALER_JSON_get_error_code2 (data,
-                                        data_size);
-    break;
   case MHD_HTTP_LENGTH_REQUIRED:
     GNUNET_break (0);
     break;
diff --git a/src/stasis/plugin_anastasis_postgres.c 
b/src/stasis/plugin_anastasis_postgres.c
index d81b27b..ded0423 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -845,6 +845,71 @@ postgres_record_recdoc_payment (
 }
 
 
+/**
+ * Record truth upload payment was made.
+ *
+ * @param cls closure
+ * @param uuid the truth's UUID
+ * @param amount the amount that was paid
+ * @param duration how long is the truth paid for
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_record_truth_upload_payment (
+  void *cls,
+  const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
+  const struct TALER_Amount *amount,
+  struct GNUNET_TIME_Relative duration)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_TIME_Absolute exp = GNUNET_TIME_relative_to_absolute 
(duration);
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (uuid),
+    TALER_PQ_query_param_amount (amount),
+    GNUNET_PQ_query_param_absolute_time (&exp),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "truth_payment_insert",
+                                             params);
+}
+
+
+/**
+ * Inquire whether truth upload payment was made.
+ *
+ * @param cls closure
+ * @param uuid the truth's UUID
+ * @param[out] paid_until set for how long this truth is paid for
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_check_truth_upload_paid (
+  void *cls,
+  const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid,
+  struct GNUNET_TIME_Absolute *paid_until)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (uuid),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_absolute_time ("expiration",
+                                         paid_until),
+    GNUNET_PQ_result_spec_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "truth_payment_select",
+                                                   params,
+                                                   rs);
+}
+
+
 /**
  * Store payment for challenge.
  *
@@ -1722,6 +1787,15 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             ") VALUES "
                             "($1, $2, $3, $4, $5);",
                             5),
+    GNUNET_PQ_make_prepare ("truth_payment_insert",
+                            "INSERT INTO anastasis_truth_payment "
+                            "(truth_uuid"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",expiration"
+                            ") VALUES "
+                            "($1, $2, $3, $4);",
+                            4),
     GNUNET_PQ_make_prepare ("recdoc_payment_done",
                             "UPDATE anastasis_recdoc_payment "
                             "SET"
@@ -1754,6 +1828,12 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             " FROM anastasis_recdoc_payment"
                             " WHERE payment_identifier=$1;",
                             1),
+    GNUNET_PQ_make_prepare ("truth_payment_select",
+                            "SELECT"
+                            " expiration"
+                            " FROM anastasis_truth_payment"
+                            " WHERE truth_uuid=$1;",
+                            1),
     GNUNET_PQ_make_prepare ("challenge_payment_select",
                             "SELECT"
                             " creation_date"
@@ -2006,6 +2086,8 @@ libanastasis_plugin_db_postgres_init (void *cls)
   plugin->create_challenge_code = &postgres_create_challenge_code;
   plugin->mark_challenge_sent = &postgres_mark_challenge_sent;
   plugin->challenge_gc = &postgres_challenge_gc;
+  plugin->record_truth_upload_payment = &postgres_record_truth_upload_payment;
+  plugin->check_truth_upload_paid = &postgres_check_truth_upload_paid;
   plugin->record_challenge_payment = &postgres_record_challenge_payment;
   plugin->check_challenge_payment = &postgres_check_challenge_payment;
   plugin->lookup_challenge_payment = &postgres_lookup_challenge_payment;
diff --git a/src/stasis/stasis-0001.sql b/src/stasis/stasis-0001.sql
index 5dd278e..8bca886 100644
--- a/src/stasis/stasis-0001.sql
+++ b/src/stasis/stasis-0001.sql
@@ -17,15 +17,27 @@
 -- Everything in one big transaction
 BEGIN;
 
--- TODO:
--- * add comments for every table and column
--- * avoid 'method' as column name (SQL keyword!)
-
-
 -- Check patch versioning is in place.
 SELECT _v.register_patch('stasis-0001', NULL, NULL);
 
 
+CREATE TABLE IF NOT EXISTS anastasis_truth_payment
+  (truth_uuid BYTEA PRIMARY KEY CHECK(LENGTH(truth_uuid)=32),
+   amount_val INT8 NOT NULL,
+   amount_frac INT4 NOT NULL,
+   expiration INT8 NOT NULL);
+COMMENT ON TABLE anastasis_truth_payment
+  IS 'Records about payments for truth uploads';
+COMMENT ON COLUMN anastasis_truth_payment.truth_uuid
+  IS 'Identifier of the truth';
+COMMENT ON COLUMN anastasis_truth_payment.amount_val
+  IS 'Amount we were paid';
+COMMENT ON COLUMN anastasis_truth_payment.amount_frac
+  IS 'Amount we were paid fraction';
+COMMENT ON COLUMN anastasis_truth_payment.expiration
+  IS 'At which date will the truth payment expire';
+
+
 CREATE TABLE IF NOT EXISTS anastasis_truth
   (truth_uuid BYTEA PRIMARY KEY CHECK(LENGTH(truth_uuid)=32),
    key_share_data BYTEA CHECK(LENGTH(key_share_data)=80) NOT NULL,
@@ -36,7 +48,7 @@ CREATE TABLE IF NOT EXISTS anastasis_truth
 COMMENT ON TABLE anastasis_truth
   IS 'Truth data is needed to authenticate clients during recovery';
 COMMENT ON COLUMN anastasis_truth.truth_uuid
-  IS 'The truth UUID uniquely identifies this truth record';
+  IS 'The truth UUID uniquely identifies this truth record. Not a foreign key 
as we may offer storing truth for free.';
 COMMENT ON COLUMN anastasis_truth.key_share_data
   IS 'Stores the encrypted key share used to recover the key (nonce, tag and 
keyshare)';
 COMMENT ON COLUMN anastasis_truth.method_name
@@ -48,6 +60,7 @@ COMMENT ON COLUMN anastasis_truth.truth_mime
 COMMENT ON COLUMN anastasis_truth.expiration
   IS 'At which date will the truth record expire';
 
+
 CREATE TABLE IF NOT EXISTS anastasis_user
   (user_id BYTEA PRIMARY KEY CHECK(LENGTH(user_id)=32),
    expiration_date INT8 NOT NULL);
@@ -58,6 +71,7 @@ COMMENT ON COLUMN anastasis_user.user_id
 COMMENT ON COLUMN anastasis_user.expiration_date
   IS 'At which date will the user record expire';
 
+
 CREATE TABLE IF NOT EXISTS anastasis_recdoc_payment
   (payment_id BIGSERIAL PRIMARY KEY,
    user_id BYTEA NOT NULL REFERENCES anastasis_user(user_id),
@@ -86,6 +100,7 @@ COMMENT ON COLUMN anastasis_recdoc_payment.creation_date
 COMMENT ON COLUMN anastasis_recdoc_payment.paid
   IS 'Is the payment finished';
 
+
 CREATE TABLE IF NOT EXISTS anastasis_challenge_payment
   (payment_id BIGSERIAL PRIMARY KEY,
    truth_uuid BYTEA CHECK(LENGTH(truth_uuid)=32) NOT NULL,
@@ -112,6 +127,7 @@ COMMENT ON COLUMN anastasis_challenge_payment.creation_date
 COMMENT ON COLUMN anastasis_challenge_payment.paid
   IS 'Is the payment finished';
 
+
 CREATE TABLE IF NOT EXISTS anastasis_recoverydocument
   (user_id BYTEA NOT NULL REFERENCES anastasis_user(user_id),
    version INT4 NOT NULL,
@@ -132,6 +148,7 @@ COMMENT ON COLUMN 
anastasis_recoverydocument.recovery_data_hash
 COMMENT ON COLUMN anastasis_recoverydocument.recovery_data
   IS 'Contains the encrypted policy and core secret';
 
+
 CREATE TABLE IF NOT EXISTS anastasis_challengecode
   (truth_uuid BYTEA PRIMARY KEY CHECK(LENGTH(truth_uuid)=32) NOT NULL,
    code INT8 NOT NULL,

-- 
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]