summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-08-23 18:22:06 +0200
committerChristian Grothoff <christian@grothoff.org>2021-08-23 18:22:06 +0200
commitd153d8036ea14503f812717b8994a4a845ab643e (patch)
tree04de8dfb53255762f6649dc680be12b0f2954929
parent4aedf96ba44ffc804749b595a1ad5d4a17d82d77 (diff)
downloadanastasis-d153d8036ea14503f812717b8994a4a845ab643e.tar.gz
anastasis-d153d8036ea14503f812717b8994a4a845ab643e.tar.bz2
anastasis-d153d8036ea14503f812717b8994a4a845ab643e.zip
-more work on iban logic
-rw-r--r--doc/sphinx/reducer.rst17
-rw-r--r--src/authorization/anastasis_authorization_plugin_iban.c14
-rw-r--r--src/backend/anastasis-httpd_truth.c29
-rw-r--r--src/cli/resources/03-backup.json12
-rw-r--r--src/cli/resources/04-backup.json12
-rw-r--r--src/cli/resources/05-backup.json12
-rw-r--r--src/cli/resources/06-backup.json12
-rwxr-xr-xsrc/cli/test_iban.sh2
-rw-r--r--src/include/anastasis_database_plugin.h26
-rw-r--r--src/reducer/anastasis_api_recovery_redux.c104
-rw-r--r--src/stasis/plugin_anastasis_postgres.c135
11 files changed, 333 insertions, 42 deletions
diff --git a/doc/sphinx/reducer.rst b/doc/sphinx/reducer.rst
index e1ca9ba..320db48 100644
--- a/doc/sphinx/reducer.rst
+++ b/doc/sphinx/reducer.rst
@@ -1641,6 +1641,8 @@ that applications must all handle. States other than ``solved`` are:
"TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
"state": "external-instructions",
"method": "iban",
+ "async": true, // optional
+ "answer_code": 987654321, // optional
"details": {
"...": "..."
}
@@ -1648,7 +1650,12 @@ that applications must all handle. States other than ``solved`` are:
}
}
- The specific instructions depend on the ``method``.
+ If "async" is "true", then the client should
+ poll for the challenge being satisfied using
+ the "answer_code" that has been provided.
+
+ The specific instructions on how to satisfy
+ the challenge depend on the ``method``.
They include:
- **iban**: The user must perform a wire transfer from their account to the Anastasis provider.
@@ -1659,14 +1666,12 @@ that applications must all handle. States other than ``solved`` are:
"challenge_amount": "EUR:1",
"credit_iban": "DE12345789000",
"business_name": "Data Loss Incorporated",
- "wire_transfer_subject": 987654321
+ "wire_transfer_subject": "Anastasis 987654321"
}
Note that the actual wire transfer subject must contain both
- the numeric ``wire_transfer_subject`` given above as well as
- the string ``Anastasis``. Thus, when generating human-readable
- instructions from the above, the display output should be
- ``Anastasis 987654321``.
+ the numeric ``answer_code`` as well as
+ the string ``Anastasis``.
**poll:**
diff --git a/src/authorization/anastasis_authorization_plugin_iban.c b/src/authorization/anastasis_authorization_plugin_iban.c
index 3c72e16..49c2ad5 100644
--- a/src/authorization/anastasis_authorization_plugin_iban.c
+++ b/src/authorization/anastasis_authorization_plugin_iban.c
@@ -328,9 +328,19 @@ respond_with_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
if (TALER_MHD_xmime_matches (mime,
"application/json"))
{
+ char subject[64];
+
+ GNUNET_snprintf (subject,
+ sizeof (subject),
+ "Anastasis %llu",
+ (unsigned long long) as->code);
resp = TALER_MHD_MAKE_JSON_PACK (
GNUNET_JSON_pack_string ("method",
"iban"),
+ GNUNET_JSON_pack_bool ("async",
+ true),
+ GNUNET_JSON_pack_uint64 ("answer_code",
+ as->code),
GNUNET_JSON_pack_object_steal (
"details",
GNUNET_JSON_PACK (
@@ -340,8 +350,8 @@ respond_with_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
ctx->business_iban),
GNUNET_JSON_pack_string ("business_name",
ctx->business_name),
- GNUNET_JSON_pack_uint64 ("wire_transfer_subject",
- as->code))));
+ GNUNET_JSON_pack_string ("wire_transfer_subject",
+ subject))));
}
else
{
diff --git a/src/backend/anastasis-httpd_truth.c b/src/backend/anastasis-httpd_truth.c
index 2c757db..3b853dc 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -1435,6 +1435,33 @@ AH_handler_truth_get (
connection);
}
/* continue with authorization plugin below */
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = db->get_unlimited_challenge_code (
+ db->cls,
+ &gc->truth_uuid,
+ gc->authorization->code_rotation_period,
+ gc->authorization->code_validity_period,
+ &gc->code);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0);
+ GNUNET_free (decrypted_truth);
+ return TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "create_challenge_code");
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* challenge code was stored successfully*/
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Got challenge code\n");
+ break;
+ }
+ }
break;
default:
GNUNET_break (0);
@@ -1491,7 +1518,7 @@ AH_handler_truth_get (
return TALER_MHD_reply_with_error (gc->connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
- "store_challenge_code");
+ "create_challenge_code");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* 0 == retry_counter of existing challenge => rate limit exceeded */
GNUNET_free (decrypted_truth);
diff --git a/src/cli/resources/03-backup.json b/src/cli/resources/03-backup.json
index 9d599d7..4dd5368 100644
--- a/src/cli/resources/03-backup.json
+++ b/src/cli/resources/03-backup.json
@@ -55,7 +55,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #1 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0",
+ "http_status": 200
},
"http://localhost:8087/": {
"methods": [
@@ -73,7 +74,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #2 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG",
+ "http_status": 200
},
"http://localhost:8088/": {
"methods": [
@@ -91,7 +93,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #3 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR",
+ "http_status": 200
},
"http://localhost:8089/": {
"methods": [
@@ -109,7 +112,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #4 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M",
+ "http_status": 200
}
},
"selected_country": "xx",
diff --git a/src/cli/resources/04-backup.json b/src/cli/resources/04-backup.json
index 15c329a..db51f5a 100644
--- a/src/cli/resources/04-backup.json
+++ b/src/cli/resources/04-backup.json
@@ -55,7 +55,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #1 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0",
+ "http_status": 200
},
"http://localhost:8087/": {
"methods": [
@@ -73,7 +74,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #2 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG",
+ "http_status": 200
},
"http://localhost:8088/": {
"methods": [
@@ -91,7 +93,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #3 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR",
+ "http_status": 200
},
"http://localhost:8089/": {
"methods": [
@@ -109,7 +112,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #4 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M",
+ "http_status": 200
}
},
"selected_country": "xx",
diff --git a/src/cli/resources/05-backup.json b/src/cli/resources/05-backup.json
index c0ce8ae..143d9e3 100644
--- a/src/cli/resources/05-backup.json
+++ b/src/cli/resources/05-backup.json
@@ -55,7 +55,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #1 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0",
+ "http_status": 200
},
"http://localhost:8087/": {
"methods": [
@@ -73,7 +74,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #2 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG",
+ "http_status": 200
},
"http://localhost:8088/": {
"methods": [
@@ -91,7 +93,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #3 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR",
+ "http_status": 200
},
"http://localhost:8089/": {
"methods": [
@@ -109,7 +112,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #4 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M",
+ "http_status": 200
}
},
"selected_country": "xx",
diff --git a/src/cli/resources/06-backup.json b/src/cli/resources/06-backup.json
index d1f0b9e..9944a17 100644
--- a/src/cli/resources/06-backup.json
+++ b/src/cli/resources/06-backup.json
@@ -55,7 +55,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #1 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0",
+ "http_status": 200
},
"http://localhost:8087/": {
"methods": [
@@ -73,7 +74,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #2 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG",
+ "http_status": 200
},
"http://localhost:8088/": {
"methods": [
@@ -91,7 +93,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #3 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR",
+ "http_status": 200
},
"http://localhost:8089/": {
"methods": [
@@ -109,7 +112,8 @@
"currency": "TESTKUDOS",
"business_name": "Data loss #4 Inc.",
"storage_limit_in_megabytes": 1,
- "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M",
+ "http_status": 200
}
},
"selected_country": "xx",
diff --git a/src/cli/test_iban.sh b/src/cli/test_iban.sh
index 777bcfb..8045db3 100755
--- a/src/cli/test_iban.sh
+++ b/src/cli/test_iban.sh
@@ -429,7 +429,7 @@ SUBJECT="Anastasis ${NUMBER}"
# FIXME-MS: must do wire transfer using $SUBJECT and $AMOUNT here!
-#bash
+bash
echo "TEST INCOMPLETE --- BAILING for now"
diff --git a/src/include/anastasis_database_plugin.h b/src/include/anastasis_database_plugin.h
index 1265f7b..079201d 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -661,6 +661,32 @@ struct ANASTASIS_DatabasePlugin
/**
+ * Setup challenge code for a given challenge identified by the
+ * challenge public key. The function will first check if there is
+ * already a valid code for this challenge present and won't insert
+ * a new one in this case. This variant is not rate-limited, will
+ * return the existing challenge if it has not yet expired and will
+ * simply create new challenges when the old ones need to be
+ * rotated.
+ *
+ * @param cls closure
+ * @param truth_uuid the identifier for the challenge
+ * @param rotation_period for how long is the code available
+ * @param validity_period for how long is the code available
+ * @param[out] code set to the code which will be checked for later
+ * @return transaction status,
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB
+ */
+ enum GNUNET_DB_QueryStatus
+ (*get_unlimited_challenge_code)(
+ void *cls,
+ const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
+ struct GNUNET_TIME_Relative rotation_period,
+ struct GNUNET_TIME_Relative validity_period,
+ uint64_t *code);
+
+
+ /**
* Remember in the database that we successfully sent a challenge.
*
* @param cls closure
diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c
index c29a08a..6b85a75 100644
--- a/src/reducer/anastasis_api_recovery_redux.c
+++ b/src/reducer/anastasis_api_recovery_redux.c
@@ -399,6 +399,51 @@ find_challenge_in_ri (json_t *state,
/**
+ * Find challenge of @a uuid in @a state under "cs".
+ *
+ * @param state the state to search
+ * @param uuid the UUID to search for
+ * @return NULL on error, otherwise challenge entry; RC is NOT incremented
+ */
+static json_t *
+find_challenge_in_cs (json_t *state,
+ const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid)
+{
+ json_t *rd = json_object_get (state,
+ "recovery_document");
+ json_t *cs = json_object_get (rd,
+ "cs");
+ json_t *c;
+ size_t off;
+
+ json_array_foreach (cs, off, c)
+ {
+ struct ANASTASIS_CRYPTO_TruthUUIDP u;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("uuid",
+ &u),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (c,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 !=
+ GNUNET_memcmp (uuid,
+ &u))
+ continue;
+ return c;
+ }
+ return NULL;
+}
+
+
+/**
* Defines a callback for the response status for a challenge start
* operation.
*
@@ -713,9 +758,17 @@ answer_feedback_cb (
const json_t *body = csr->details.external_challenge;
const char *method;
json_t *details;
+ bool is_async = false;
+ uint64_t code = 0;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("method",
&method),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("async",
+ &is_async)),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint64 ("answer_code",
+ &code)),
GNUNET_JSON_spec_json ("details",
&details),
GNUNET_JSON_spec_end ()
@@ -741,7 +794,31 @@ answer_feedback_cb (
err));
return;
}
+ if (is_async)
+ {
+ json_t *c = find_challenge_in_cs (sctx->state,
+ &cd->uuid);
+ if (NULL == c)
+ {
+ GNUNET_break (0);
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_ERROR);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (c,
+ "async",
+ json_true ()));
+ GNUNET_assert (0 ==
+ json_object_set_new (c,
+ "answer-pin",
+ json_integer (code)));
+ }
reply = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
"external-instructions"),
@@ -815,10 +892,11 @@ solve_challenge_cb (void *cls,
return;
}
- if (GNUNET_OK !=
- GNUNET_JSON_parse (sctx->args,
- tspec,
- NULL, NULL))
+ if ( (NULL != sctx->args) &&
+ (GNUNET_OK !=
+ GNUNET_JSON_parse (sctx->args,
+ tspec,
+ NULL, NULL)) )
{
GNUNET_break_op (0);
ANASTASIS_redux_fail_ (sctx->cb,
@@ -845,7 +923,7 @@ solve_challenge_cb (void *cls,
(! cd->async) )
continue;
- challenge = find_challenge_in_ri (sctx->state,
+ challenge = find_challenge_in_cs (sctx->state,
&cd->uuid);
if (NULL == challenge)
{
@@ -963,6 +1041,7 @@ solve_challenge_cb (void *cls,
struct ANASTASIS_Challenge *ci = ri->cs[i];
const struct ANASTASIS_ChallengeDetails *cd;
int ret;
+ json_t *c;
cd = ANASTASIS_challenge_get_details (ci);
if (cd->async)
@@ -980,6 +1059,9 @@ solve_challenge_cb (void *cls,
sctx_free (sctx);
return;
}
+ c = find_challenge_in_cs (sctx->state,
+ &cd->uuid);
+ GNUNET_assert (NULL != c);
if (0 == strcmp ("question",
cd->type))
{
@@ -999,7 +1081,7 @@ solve_challenge_cb (void *cls,
}
/* persist answer, in case payment is required */
GNUNET_assert (0 ==
- json_object_set (challenge,
+ json_object_set (c,
"answer",
janswer));
ret = ANASTASIS_challenge_answer (ci,
@@ -1023,7 +1105,7 @@ solve_challenge_cb (void *cls,
/* persist answer, in case async processing
happens via poll */
GNUNET_assert (0 ==
- json_object_set (challenge,
+ json_object_set (c,
"answer-pin",
pin));
ret = ANASTASIS_challenge_answer2 (ci,
@@ -1316,14 +1398,6 @@ poll_challenges (json_t *state,
= GNUNET_new (struct SelectChallengeContext);
json_t *rd;
- if (NULL == arguments)
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "arguments missing");
- return NULL;
- }
rd = json_object_get (state,
"recovery_document");
if (NULL == rd)
diff --git a/src/stasis/plugin_anastasis_postgres.c b/src/stasis/plugin_anastasis_postgres.c
index 8c06243..506c304 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -38,6 +38,12 @@
*/
#define MAX_RETRIES 3
+/**
+ * Maximum value allowed for nonces. Limited to 2^52 to ensure the
+ * numeric value survives a conversion to float by JavaScript.
+ */
+#define NONCE_MAX_VALUE (1LLU << 52)
+
/**
* Type of the "cls" argument given to each of the functions in
@@ -2125,7 +2131,7 @@ postgres_create_challenge_code (
}
*code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
- INT64_MAX);
+ NONCE_MAX_VALUE);
*retransmission_date = GNUNET_TIME_UNIT_ZERO_ABS;
{
struct GNUNET_PQ_QueryParam params[] = {
@@ -2169,6 +2175,132 @@ retry:
/**
+ * Setup challenge code for a given challenge identified by the
+ * challenge public key. The function will first check if there is
+ * already a valid code for this challenge present and won't insert
+ * a new one in this case. This variant is not rate-limited, will
+ * return the existing challenge if it has not yet expired and will
+ * simply create new challenges when the old ones need to be
+ * rotated.
+ *
+ * @param cls closure
+ * @param truth_uuid the identifier for the challenge
+ * @param rotation_period for how long is the code available
+ * @param validity_period for how long is the code available
+ * @param[out] code set to the code which will be checked for later
+ * @return transaction status,
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_unlimited_challenge_code (
+ void *cls,
+ const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
+ struct GNUNET_TIME_Relative rotation_period,
+ struct GNUNET_TIME_Relative validity_period,
+ uint64_t *code)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ struct GNUNET_TIME_Absolute expiration_date;
+ struct GNUNET_TIME_Absolute ex_rot;
+
+ check_connection (pg);
+ GNUNET_TIME_round_abs (&now);
+ expiration_date = GNUNET_TIME_absolute_add (now,
+ validity_period);
+ ex_rot = GNUNET_TIME_absolute_subtract (now,
+ rotation_period);
+ for (unsigned int retries = 0; retries<MAX_RETRIES; retries++)
+ {
+ if (GNUNET_OK !=
+ begin_transaction (pg,
+ "get_unlimited_challenge_code"))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (truth_uuid),
+ TALER_PQ_query_param_absolute_time (&now),
+ TALER_PQ_query_param_absolute_time (&ex_rot),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("code",
+ code),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "challengecode_select_meta",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ rollback (pg);
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ goto retry;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* no active challenge, create fresh one (below) */
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ rollback (pg);
+ return qs;
+ }
+ }
+
+ *code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ NONCE_MAX_VALUE);
+ {
+ uint32_t retry_counter = UINT32_MAX;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (truth_uuid),
+ GNUNET_PQ_query_param_uint64 (code),
+ TALER_PQ_query_param_absolute_time (&now),
+ TALER_PQ_query_param_absolute_time (&expiration_date),
+ GNUNET_PQ_query_param_uint32 (&retry_counter),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "challengecode_insert",
+ params);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ goto retry;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0);
+ rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ }
+ qs = commit_transaction (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto retry;
+ if (qs < 0)
+ return qs;
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+retry:
+ rollback (pg);
+ }
+ return GNUNET_DB_STATUS_SOFT_ERROR;
+}
+
+
+/**
* Remember in the database that we successfully sent a challenge.
*
* @param cls closure
@@ -2675,6 +2807,7 @@ libanastasis_plugin_db_postgres_init (void *cls)
plugin->test_challenge_code_satisfied =
&postgres_test_challenge_code_satisfied;
plugin->create_challenge_code = &postgres_create_challenge_code;
+ plugin->get_unlimited_challenge_code = &postgres_get_unlimited_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;