aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-10-14 18:56:59 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2022-10-14 18:56:59 +0200
commit054e157af8c768062dd0a8e66614da18407fca28 (patch)
treefcd792c6ed7f4384e9b29f8e200f62837cd00f1e
parent956e3c3065ddb762dbe01fd720cc5ef403232564 (diff)
WIP: policy_details handling continued
- policy details generated on deposit/batch-deposit requests - insert or update of policy details in the DB - accumulation of amounts of multiple deposits for the same policy_details
-rw-r--r--src/auditor/taler-auditor-sync.c1
-rw-r--r--src/auditor/taler-helper-auditor-coins.c4
-rw-r--r--src/exchange/taler-exchange-httpd.c141
-rw-r--r--src/exchange/taler-exchange-httpd_batch-deposit.c91
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c100
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c166
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.h15
-rw-r--r--src/exchange/taler-exchange-httpd_metrics.h3
-rw-r--r--src/exchangedb/exchange-0001-part.sql111
-rw-r--r--src/exchangedb/irbt_callbacks.c16
-rw-r--r--src/exchangedb/lrbt_callbacks.c106
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c302
-rw-r--r--src/exchangedb/procedures.sql152
-rw-r--r--src/exchangedb/test_exchangedb.c1
-rw-r--r--src/extensions/age_restriction/age_restriction.c2
-rw-r--r--src/extensions/extensions.c83
-rw-r--r--src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c225
-rw-r--r--src/include/taler_exchangedb_plugin.h117
-rw-r--r--src/include/taler_extensions.h201
-rw-r--r--src/include/taler_extensions_policy.h199
20 files changed, 1252 insertions, 784 deletions
diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c
index bbe257ac..ebc38d10 100644
--- a/src/auditor/taler-auditor-sync.c
+++ b/src/auditor/taler-auditor-sync.c
@@ -115,7 +115,6 @@ static struct Table tables[] = {
{ .rt = TALER_EXCHANGEDB_RT_EXTENSIONS},
{ .rt = TALER_EXCHANGEDB_RT_POLICY_DETAILS },
{ .rt = TALER_EXCHANGEDB_RT_POLICY_FULFILMENTS },
- { .rt = TALER_EXCHANGEDB_RT_POLICY_DETAILS_FULFILMENTS },
{ .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS},
{ .rt = TALER_EXCHANGEDB_RT_PURSE_REFUNDS},
{ .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES},
diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c
index af38b0cf..b1650fd7 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -1626,8 +1626,8 @@ deposit_cb (void *cls,
&h_wire,
&deposit->h_contract_terms,
&deposit->coin.h_age_commitment,
- deposit->has_policy_details ?
- &deposit->h_policy :NULL, &h_denom_pub,
+ deposit->has_policy ? &deposit->h_policy : NULL,
+ &h_denom_pub,
deposit->timestamp,
&deposit->merchant_pub,
deposit->refund_deadline,
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 32cbd8dd..1cbaee96 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -1041,138 +1041,6 @@ handle_post_auditors (struct TEH_RequestContext *rc,
root);
}
-
-/**
- * Handle GET "/extensions/..." requests.
- *
- * @param rc request context
- * @param args array of additional options
- * @return MHD result code
- */
-static MHD_RESULT
-handle_get_extensions (struct TEH_RequestContext *rc,
- const char *const args[])
-{
- const struct TALER_Extension *ext = NULL;
-
- if (NULL == args[0])
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION");
- }
-
- ext = TALER_extensions_get_by_name (args[0]);
- if (NULL == ext)
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION unknown");
- }
-
- if (NULL == ext->policy_get_handler)
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_IMPLEMENTED,
- TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
- "GET /extensions/$EXTENSION not supported");
-
- return ext->policy_get_handler (
- rc->connection,
- &args[1]);
-}
-
-
-/* @brief function pointer for TALER_extension.check */
-static enum GNUNET_GenericReturnValue
-check_serial_ids (
- struct GNUNET_HashCode serial_ids[],
- size_t size)
-{
- for (size_t i = 0; i < size; i++)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Got to check serial_id: %s\n",
- GNUNET_h2s (&serial_ids[i]));
- return GNUNET_OK;
-}
-
-
-/**
- * Handle POST "/extensions/..." requests.
- *
- * @param rc request context
- * @param root uploaded JSON data
- * @param args array of additional options
- * @return MHD result code
- */
-static MHD_RESULT
-handle_post_extensions (struct TEH_RequestContext *rc,
- const json_t *root,
- const char *const args[])
-{
- const struct TALER_Extension *ext = NULL;
- json_t *output;
-
- if (NULL == args[0])
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION");
- }
-
- ext = TALER_extensions_get_by_name (args[0]);
- if (NULL == ext)
- {
- GNUNET_break_op (0);
- return r404 (rc->connection,
- "/extensions/$EXTENSION unknown");
- }
-
- if (NULL == ext->policy_post_handler)
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_IMPLEMENTED,
- TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
- "POST /extensions/$EXTENSION not supported");
-
- {
- enum GNUNET_GenericReturnValue ret;
- struct TALER_PolicyFulfilmentOutcome *outcome;
-
- ret = ext->policy_post_handler (root,
- &args[1],
- &check_serial_ids,
- &outcome,
- &output);
-
- if (GNUNET_OK != ret)
- {
- TALER_policy_fulfilment_outcome_free (outcome);
- TALER_MHD_reply_json_steal (
- rc->connection,
- output,
- MHD_HTTP_BAD_REQUEST);
- }
-
- /* TODO: deal with outcome */
- {
- for (size_t i = 0; i < outcome->len; i++)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Got outcome for serial_id: %s, state: %s, amount: %s\n",
- GNUNET_h2s (&outcome->positions[i].serial_id),
- TALER_policy_fulfilment_state_str (
- outcome->positions[i].state),
- TALER_amount_to_string (&outcome->positions[i].new_amount));
- }
- }
-
- }
-
- return TALER_MHD_reply_json_steal (rc->connection,
- output,
- MHD_HTTP_OK);
-}
-
-
/**
* Handle incoming HTTP request.
*
@@ -1393,15 +1261,8 @@ handle_mhd_request (void *cls,
/* extensions endpoints */
{
.url = "extensions",
- .method = MHD_HTTP_METHOD_GET,
- .handler.get = &handle_get_extensions,
- .nargs = 4, /* Arbitrary upper bound */
- .nargs_is_upper_bound = true,
- },
- {
- .url = "extensions",
.method = MHD_HTTP_METHOD_POST,
- .handler.post = &handle_post_extensions,
+ .handler.post = &TEH_extensions_post_handler,
.nargs = 4, /* Arbitrary upper bound */
.nargs_is_upper_bound = true,
},
diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c b/src/exchange/taler-exchange-httpd_batch-deposit.c
index d4a9666e..14267e2a 100644
--- a/src/exchange/taler-exchange-httpd_batch-deposit.c
+++ b/src/exchange/taler-exchange-httpd_batch-deposit.c
@@ -90,8 +90,12 @@ struct BatchDepositContext
* Additional details for policy relevant for this
* deposit operation, possibly NULL!
*/
- json_t *policy_details;
- bool has_policy_details;
+ json_t *policy_json;
+
+ /**
+ * Will be true if policy_json were provided
+ */
+ bool has_policy;
/**
* Hash over @e policy_details, might be all zero;
@@ -99,6 +103,19 @@ struct BatchDepositContext
struct TALER_ExtensionPolicyHashP h_policy;
/**
+ * If @e policy_json was present, the corresponding policy extension
+ * calculates these details. These will be persisted in the policy_details
+ * table.
+ */
+ struct TALER_PolicyDetails policy_details;
+
+ /**
+ * When @e policy_details are persisted, this contains the id of the record
+ * in the policy_details table.
+ */
+ uint64_t policy_details_serial_id;
+
+ /**
* Time when this request was generated. Used, for example, to
* assess when (roughly) the income was achieved for tax purposes.
* Note that the Exchange will only check that the timestamp is not "too
@@ -174,7 +191,7 @@ again:
&TEH_keys_exchange_sign_,
&bdc->h_contract_terms,
&bdc->h_wire,
- bdc->has_policy_details ? &bdc->h_policy : NULL,
+ bdc->has_policy ? &bdc->h_policy : NULL,
bdc->exchange_timestamp,
bdc->wire_deadline,
bdc->refund_deadline,
@@ -247,6 +264,21 @@ batch_deposit_transaction (void *cls,
bool balance_ok;
bool in_conflict;
+
+ /* If the deposit has a policy associated to it, persist it. This will
+ * insert or update the record. */
+ if (dc->has_policy)
+ {
+ qs = TEH_plugin->persist_policy_details (TEH_plugin->cls,
+ &dc->policy_details,
+ &dc->policy_details_serial_id,
+ &dc->policy_details.accumulated_total,
+ &dc->policy_details.fulfillment_state);
+ if (qs < 0)
+ return qs;
+ }
+
+ /* deposit the individutal coins */
for (unsigned int i = 0; i<dc->num_coins; i++)
{
const struct TALER_EXCHANGEDB_Deposit *deposit = &dc->deposits[i];
@@ -258,13 +290,19 @@ batch_deposit_transaction (void *cls,
mhd_ret);
if (qs < 0)
return qs;
- qs = TEH_plugin->do_deposit (TEH_plugin->cls,
- deposit,
- known_coin_id,
- &dc->h_payto,
- &dc->exchange_timestamp,
- &balance_ok,
- &in_conflict);
+
+ qs = TEH_plugin->do_deposit (
+ TEH_plugin->cls,
+ deposit,
+ known_coin_id,
+ &dc->h_payto,
+ (dc->has_policy)
+ ? &dc->policy_details_serial_id
+ : NULL,
+ &dc->exchange_timestamp,
+ &balance_ok,
+ &in_conflict);
+
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -474,7 +512,7 @@ parse_coin (struct MHD_Connection *connection,
&dc->h_wire,
&dc->h_contract_terms,
&deposit->coin.h_age_commitment,
- dc->has_policy_details ? &dc->h_policy :
+ dc->has_policy ? &dc->h_policy :
NULL,
&deposit->coin.denom_pub_hash,
dc->timestamp,
@@ -501,7 +539,7 @@ parse_coin (struct MHD_Connection *connection,
but rather insert them ONCE and then per-coin only use
the resulting extension UUID/serial; so the data structure
here should be changed once we look at extensions in earnest. */
- deposit->policy_details = dc->policy_details;
+ deposit->policy_json = dc->policy_json;
deposit->timestamp = dc->timestamp;
deposit->refund_deadline = dc->refund_deadline;
deposit->wire_deadline = dc->wire_deadline;
@@ -518,7 +556,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
struct BatchDepositContext dc;
json_t *coins;
bool no_refund_deadline = true;
- bool no_policy_details = true;
+ bool no_policy_json = true;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("merchant_payto_uri",
&dc.payto_uri),
@@ -532,8 +570,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
&coins),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("policy",
- &dc.policy_details),
- &no_policy_details),
+ &dc.policy_json),
+ &no_policy_json),
GNUNET_JSON_spec_timestamp ("timestamp",
&dc.timestamp),
GNUNET_JSON_spec_mark_optional (
@@ -564,7 +602,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
return MHD_YES; /* failure */
}
- dc.has_policy_details = ! no_policy_details;
+ dc.has_policy = ! no_policy_json;
/* validate merchant's wire details (as far as we can) */
{
@@ -610,11 +648,26 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
TALER_merchant_wire_signature_hash (dc.payto_uri,
&dc.wire_salt,
&dc.h_wire);
- if (dc.has_policy_details)
+
+ /* handle policy, if present */
+ if (dc.has_policy)
{
- TALER_deposit_policy_hash (dc.policy_details,
+ const char *error_hint = NULL;
+
+ if (GNUNET_OK !=
+ TALER_extensions_create_policy_details (
+ dc.policy_json,
+ &dc.policy_details,
+ &error_hint))
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
+ error_hint);
+
+ TALER_deposit_policy_hash (dc.policy_json,
&dc.h_policy);
}
+
dc.num_coins = json_array_size (coins);
if (0 == dc.num_coins)
{
@@ -651,6 +704,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
GNUNET_JSON_parse_free (spec);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
+
+ /* FIXME: sum all contributions for the policy_details.accumulated_total */
}
dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 12741c6b..f44775c0 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -132,6 +132,12 @@ struct DepositContext
*/
uint64_t known_coin_id;
+ /**
+ * When deposit->has_policy is true, and deposit->policy_details are
+ * persisted, this contains the id of the record in the policy_details table.
+ */
+ uint64_t policy_details_serial_id;
+
};
@@ -165,14 +171,36 @@ deposit_transaction (void *cls,
if (qs < 0)
return qs;
+ /* If the deposit has a policy associated to it, persist it. This will
+ * insert or update the record. */
+ if (dc->deposit->has_policy)
+ {
+ struct TALER_Amount accumulated_total;
+ enum TALER_PolicyFulfillmentState fulfillment_state;
+ qs = TEH_plugin->persist_policy_details (TEH_plugin->cls,
+ &dc->deposit->policy_details,
+ &dc->policy_details_serial_id,
+ &accumulated_total,
+ &fulfillment_state);
+
+ /* FIXME: what to do with accumulated_total and fulfillment_state ? */
+
+ if (qs < 0)
+ return qs;
+ }
+
+ qs = TEH_plugin->do_deposit (
+ TEH_plugin->cls,
+ dc->deposit,
+ dc->known_coin_id,
+ &dc->h_payto,
+ (dc->deposit->has_policy)
+ ? &dc->policy_details_serial_id
+ : NULL,
+ &dc->exchange_timestamp,
+ &balance_ok,
+ &in_conflict);
- qs = TEH_plugin->do_deposit (TEH_plugin->cls,
- dc->deposit,
- dc->known_coin_id,
- &dc->h_payto,
- &dc->exchange_timestamp,
- &balance_ok,
- &in_conflict);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -219,7 +247,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Deposit deposit;
const char *payto_uri;
struct TALER_ExtensionPolicyHashP *ph_policy = NULL;
- bool no_policy_details;
+ bool no_policy_json;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("merchant_payto_uri",
&payto_uri),
@@ -255,8 +283,8 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.wire_deadline),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("policy",
- &deposit.policy_details),
- &no_policy_details),
+ &deposit.policy_json),
+ &no_policy_json),
GNUNET_JSON_spec_end ()
};
struct TALER_MerchantWireHashP h_wire;
@@ -284,7 +312,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
}
/* set the state of the policy presence in the deposit struct */
- deposit.has_policy_details = ! no_policy_details;
+ deposit.has_policy = ! no_policy_json;
/* validate merchant's wire details (as far as we can) */
{
@@ -333,28 +361,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&h_wire);
dc.deposit = &deposit;
- /* Check policy */
- if (deposit.has_policy_details)
- {
- const char *error_hint = NULL;
- enum TALER_PolicyFulfilmentState state_on_timeout;
-
- if (GNUNET_OK !=
- TALER_extensions_extract_meta_data_from_policy_details (
- deposit.policy_details,
- &deposit.policy_serial_id,
- &deposit.policy_deadline,
- &state_on_timeout,
- &error_hint))
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
- error_hint);
-
- GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state_on_timeout);
- deposit.policy_state_on_timeout = state_on_timeout;
- }
-
/* new deposit */
dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
/* check denomination exists and is valid */
@@ -420,13 +426,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
NULL);
}
- if (deposit.has_policy_details)
- {
- TALER_deposit_policy_hash (deposit.policy_details,
- &deposit.h_policy);
- ph_policy = &deposit.h_policy;
- }
-
deposit.deposit_fee = dk->meta.fees.deposit;
/* check coin signature */
switch (dk->denom_pub.cipher)
@@ -463,6 +462,27 @@ TEH_handler_deposit (struct MHD_Connection *connection,
NULL);
}
+ /* Check policy input and create policy details */
+ if (deposit.has_policy)
+ {
+ const char *error_hint = NULL;
+
+ if (GNUNET_OK !=
+ TALER_extensions_create_policy_details (
+ deposit.policy_json,
+ &deposit.policy_details,
+ &error_hint))
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
+ error_hint);
+
+ TALER_deposit_policy_hash (deposit.policy_json,
+ &deposit.h_policy);
+ ph_policy = &deposit.h_policy;
+ }
+
+
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
TALER_wallet_deposit_verify (&deposit.amount_with_fee,
diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c
index e8d3e4af..8e8f0e01 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -14,7 +14,7 @@
*/
/**
* @file taler-exchange-httpd_extensions.c
- * @brief Handle extensions (age-restriction, peer2peer)
+ * @brief Handle extensions (age-restriction, policy extensions)
* @author Özgür Kesim
*/
#include "platform.h"
@@ -232,4 +232,168 @@ TEH_extensions_done ()
}
+/*
+ * @brief Execute database transactions for /extensions/policy_* POST requests.
+ *
+ * @param cls a `struct TALER_PolicyFulfillmentOutcome`
+ * @param connection MHD request context
+ * @param[out] mhd_ret set to MHD status on error
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+policy_fulfillment_transaction (
+ void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct TALER_PolicyFulfillmentTransactionData *fulfillment = cls;
+
+ return TEH_plugin->add_policy_fulfillment_proof (TEH_plugin->cls,
+ fulfillment);
+}
+
+
+MHD_RESULT
+TEH_extensions_post_handler (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[])
+{
+ const struct TALER_Extension *ext = NULL;
+ json_t *output;
+ struct TALER_PolicyDetails *policy_details = NULL;
+ size_t policy_details_count = 0;
+
+
+ if (NULL == args[0])
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "/extensions/$EXTENSION");
+ }
+
+ ext = TALER_extensions_get_by_name (args[0]);
+ if (NULL == ext)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "/extensions/$EXTENSION unknown");
+ }
+
+ if (NULL == ext->policy_post_handler)
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "POST /extensions/$EXTENSION not supported");
+
+ /* Extract hash_codes and retrieve related policy_details from the DB */
+ {
+ enum GNUNET_GenericReturnValue ret;
+ enum GNUNET_DB_QueryStatus qs;
+ const char *error_msg;
+ struct GNUNET_HashCode *hcs;
+ size_t len;
+ json_t*val;
+ size_t idx;
+ json_t *jhash_codes = json_object_get (root,
+ "policy_hash_codes");
+ if (! json_is_array (jhash_codes))
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "policy_hash_codes are missing");
+
+ len = json_array_size (jhash_codes);
+ hcs = GNUNET_new_array (len,
+ struct GNUNET_HashCode);
+ policy_details = GNUNET_new_array (len,
+ struct TALER_PolicyDetails);
+
+ json_array_foreach (jhash_codes, idx, val)
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, &hcs[idx]),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ret = GNUNET_JSON_parse (val,
+ spec,
+ &error_msg,
+ NULL);
+ if (GNUNET_OK != ret)
+ break;
+
+ qs = TEH_plugin->get_policy_details (TEH_plugin->cls,
+ &hcs[idx],
+ &policy_details[idx]);
+ if (qs < 0)
+ {
+ error_msg = "a policy_hash_code couldn't be found";
+ break;
+ }
+ }
+
+ GNUNET_free (hcs);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_free (policy_details);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ error_msg);
+ }
+ }
+
+
+ {
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = ext->policy_post_handler (root,
+ &args[1],
+ policy_details,
+ policy_details_count,
+ &output);
+
+ if (GNUNET_OK != ret)
+ {
+ TALER_MHD_reply_json_steal (
+ rc->connection,
+ output,
+ MHD_HTTP_BAD_REQUEST);
+ }
+
+ /* execute fulfillment transaction */
+ {
+ MHD_RESULT mhd_ret;
+ struct TALER_PolicyFulfillmentTransactionData fulfillment = {
+ .proof = root,
+ .timestamp = GNUNET_TIME_timestamp_get (),
+ .details = policy_details,
+ .details_count = policy_details_count
+ };
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "execute policy fulfillment",
+ TEH_MT_REQUEST_POLICY_FULFILMENT,
+ &mhd_ret,
+ &policy_fulfillment_transaction,
+ &fulfillment))
+ {
+ json_decref (output);
+ return mhd_ret;
+ }
+ }
+ }
+
+ return TALER_MHD_reply_json_steal (rc->connection,
+ output,
+ MHD_HTTP_OK);
+}
+
+
/* end of taler-exchange-httpd_extensions.c */
diff --git a/src/exchange/taler-exchange-httpd_extensions.h b/src/exchange/taler-exchange-httpd_extensions.h
index 4659b653..e435f8f0 100644
--- a/src/exchange/taler-exchange-httpd_extensions.h
+++ b/src/exchange/taler-exchange-httpd_extensions.h
@@ -40,4 +40,19 @@ TEH_extensions_init (void);
void
TEH_extensions_done (void);
+
+/**
+ * Handle POST "/extensions/..." requests.
+ *
+ * @param rc request context
+ * @param root uploaded JSON data
+ * @param args array of additional options
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_extensions_post_handler (
+ struct TEH_RequestContext *rc,
+ const json_t *root,
+ const char *const args[]);
+
#endif
diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h
index c1b7326f..5acd3605 100644
--- a/src/exchange/taler-exchange-httpd_metrics.h
+++ b/src/exchange/taler-exchange-httpd_metrics.h
@@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest
TEH_MT_REQUEST_IDEMPOTENT_MELT = 10,
TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11,
TEH_MT_REQUEST_BATCH_DEPOSIT = 12,
- TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */
+ TEH_MT_REQUEST_POLICY_FULFILMENT = 13,
+ TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
};
/**
diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql
index 4a116d12..c57eb454 100644
--- a/src/exchangedb/exchange-0001-part.sql
+++ b/src/exchangedb/exchange-0001-part.sql
@@ -536,76 +536,69 @@ CREATE TABLE IF NOT EXISTS refresh_transfer_keys_default
SELECT add_constraints_to_refresh_transfer_keys_partition('default');
--- ------------------------------ policy_fulfilments -------------------------------------
-
-CREATE TABLE IF NOT EXISTS policy_fulfilments
- (fulfilment_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
- ,fulfilment_timestamp INT8 NOT NULL
- ,fulfilment_proof VARCHAR)
- PARTITION BY HASH (fulfilment_id);
-COMMENT ON TABLE policy_fulfilments
- IS 'Proofs of fulfilment of policies that were set in deposits';
-COMMENT ON COLUMN policy_fulfilments.fulfilment_timestamp
- IS 'Timestamp of the arrival of a proof of fulfilment';
-COMMENT ON COLUMN policy_fulfilments.fulfilment_proof
- IS 'JSON object with a proof of the fulfilment of a policy. Supported details depend on the policy extensions supported by the exchange.';
-
-CREATE TABLE IF NOT EXISTS policy_fulfilments_default
- PARTITION OF policy_fulfilments
- FOR VALUES WITH (MODULUS 1, REMAINDER 0);
+-- ------------------------------ policy_fulfillments -------------------------------------
+
+CREATE TABLE IF NOT EXISTS policy_fulfillments
+ (fulfillment_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY
+ ,fulfillment_timestamp INT8 NOT NULL
+ ,fulfillment_proof VARCHAR
+ ,h_fulfillment_proof BYTEA NOT NULL CHECK(LENGTH(h_fulfillment_proof) = 64) UNIQUE
+ ,policy_hash_codes BYTEA NOT NULL CHECK(0 = MOD(LENGTH(policy_hash_codes), 16))
+ );
+COMMENT ON TABLE policy_fulfillments
+ IS 'Proofs of fulfillment of policies that were set in deposits';
+COMMENT ON COLUMN policy_fulfillments.fulfillment_timestamp
+ IS 'Timestamp of the arrival of a proof of fulfillment';
+COMMENT ON COLUMN policy_fulfillments.fulfillment_proof
+ IS 'JSON object with a proof of the fulfillment of a policy. Supported details depend on the policy extensions supported by the exchange.';
+COMMENT ON COLUMN policy_fulfillments.h_fulfillment_proof
+ IS 'Hash of the fulfillment_proof';
+COMMENT ON COLUMN policy_fulfillments.policy_hash_codes
+ IS 'Concatenation of the policy_hash_code of all policy_details that are fulfilled by this proof';
-- ------------------------------ policy_details ----------------------------------------
CREATE TABLE IF NOT EXISTS policy_details
(policy_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
- ,serial_id BYTEA PRIMARY KEY CHECK(LENGTH(serial_id)=64)
- ,policy_options VARCHAR
+ ,policy_hash_code BYTEA PRIMARY KEY CHECK(LENGTH(policy_hash_code)=16)
+ ,policy_json VARCHAR
,deadline INT8 NOT NULL
- ,timeout_fulfilment_state smallint NOT NULL CHECK(timeout_fulfilment_state in (5, 6))
- ,fulfilment_state smallint NOT NULL CHECK(fulfilment_state between 0 and 6))
- PARTITION BY HASH (serial_id);
+ ,commitment_val INT8 NOT NULL
+ ,commitment_frac INT4 NOT NULL
+ ,accumulated_total_val INT8 NOT NULL
+ ,accumulated_total_frac INT4 NOT NULL
+ ,fee_val INT8 NOT NULL
+ ,fee_frac INT4 NOT NULL
+ ,transferable_val INT8 NOT NULL
+ ,transferable_frac INT8 NOT NULL
+ ,fulfillment_state smallint NOT NULL CHECK(fulfillment_state between 0 and 5)
+ ,fulfillment_id BIGINT NULL REFERENCES policy_fulfillments (fulfillment_id) ON DELETE CASCADE
+ );
COMMENT ON TABLE policy_details
IS 'Policies that were provided with deposits via policy extensions.';
-COMMENT ON COLUMN policy_details.serial_id
+COMMENT ON COLUMN policy_details.policy_hash_code
IS 'ID (GNUNET_HashCode) that identifies a policy. Will be calculated by the policy extension based on the content';
-COMMENT ON COLUMN policy_details.policy_options
+COMMENT ON COLUMN policy_details.policy_json
IS 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the policy extensions supported by the exchange.';
COMMENT ON COLUMN policy_details.deadline
- IS 'Deadline until the policy must be marked as fulfilled or unfulfilled (maybe "forever")';
-COMMENT ON COLUMN policy_details.timeout_fulfilment_state
- IS 'State that a pending policy should be put into, once the deadline is reached. Allowed values are 5 (TIMEOUT, transfer coins) or 6 (TIMEOUT, coins refreshable)';
-COMMENT ON COLUMN policy_details.fulfilment_state
- IS 'State of the fulfilment:
- - 0 (PENDING)
- - 1 (SUCCESS, transfer coins)
- - 2 (SUCCESS, coins refreshable)
- - 3 (FAILURE, transfer coins)
- - 4 (FAILURE, coins refreshable)
- - 5 (TIMEOUT, tranfer coins)
- - 6 (TIMEOUT, coins refrehsable)';
-
-CREATE TABLE IF NOT EXISTS policy_details_default
- PARTITION OF policy_details
- FOR VALUES WITH (MODULUS 1, REMAINDER 0);
-
--- ------------------------------ policy_details_fulfilments -----------------------------
-
-CREATE TABLE IF NOT EXISTS policy_details_fulfilments
- (fulfilment_id BIGINT NOT NULL REFERENCES policy_fulfilments(fulfilment_id) ON DELETE CASCADE
- ,serial_id BYTEA NOT NULL UNIQUE REFERENCES policy_details(serial_id) ON DELETE CASCADE)
- PARTITION BY HASH (serial_id); -- FIXME: choose other thing to hash here?
--- FIXME: define a primary key here?
-COMMENT ON TABLE policy_details_fulfilments
- IS 'Links policy_details.serial_id''s with policy_fulfilments.id''s. The same proof of fulfilment can be associated with multiple serial-id''s';
-COMMENT ON COLUMN policy_details_fulfilments.fulfilment_id
- IS 'ID of the proof of fulfilment';
-COMMENT ON COLUMN policy_details_fulfilments.serial_id
- IS 'Serial-ID of the corresponding policy_detail';
-
-CREATE TABLE IF NOT EXISTS policy_details_fulfilments_default
- PARTITION OF policy_details_fulfilments
- FOR VALUES WITH (MODULUS 1, REMAINDER 0);
-
+ IS 'Deadline until the policy must be marked as fulfilled (maybe "forever")';
+COMMENT ON COLUMN policy_details.commitment_val
+ IS 'The amount that this policy commits to. Invariant: commitment >= fee';
+COMMENT ON COLUMN policy_details.accumulated_total_val
+ IS 'The sum of all contributions of all deposit that reference this policy. Invariant: The fulfilment_state must be Insufficient as long as accumulated_total < commitment';
+COMMENT ON COLUMN policy_details.fee_val
+ IS 'The fee for this policy, due when the policy is fulfilled or timed out';
+COMMENT ON COLUMN policy_details.transferable_val
+ IS 'The amount that on fulfilment or timeout will be transfered to the payto-URI''s of the corresponding deposit''s. The policy fees must have been already deducted from it. Invariant: fee+transferable <= accumulated_total. The remaining amount (accumulated_total - fee - transferable) can be refreshed by the owner of the coins when the state is Timeout or Success.';
+COMMENT ON COLUMN policy_details.fulfillment_state
+ IS 'State of the fulfillment:
+ - 0 (Failure)
+ - 1 (Insufficient)
+ - 2 (Ready)
+ - 4 (Success)
+ - 5 (Timeout)';
+COMMENT ON COLUMN policy_details.fulfillment_id
+ IS 'Reference to the proof of the fulfillment of this policy, if it exists. Invariant: If not NULL, this entry''s .hash_code MUST be part of the corresponding policy_fulfillments.policy_hash_codes array.';
-- ------------------------------ deposits ----------------------------------------
diff --git a/src/exchangedb/irbt_callbacks.c b/src/exchangedb/irbt_callbacks.c
index e9cce43c..b526b5cb 100644
--- a/src/exchangedb/irbt_callbacks.c
+++ b/src/exchangedb/irbt_callbacks.c
@@ -929,16 +929,18 @@ irbt_cb_table_policy_details (struct PostgresClosure *pg,
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
- NULL == td->details.policy_details.policy_options ?
- GNUNET_PQ_query_param_null () :
- GNUNET_PQ_query_param_string (
- td->details.policy_details.policy_options),
+ ((NULL == td->details.policy_details.policy_json) ||
+ (td->details.policy_details.no_policy_json))
+ ? GNUNET_PQ_query_param_null ()
+ : TALER_PQ_query_param_json (td->details.policy_details.policy_json),
GNUNET_PQ_query_param_timestamp (
&td->details.policy_details.deadline),
GNUNET_PQ_query_param_uint16 (
- &td->details.policy_details.timeout_fulfilment_state),
- GNUNET_PQ_query_param_uint16 (
- &td->details.policy_details.fulfilment_state),
+ &td->details.policy_details.fulfillment_state),
+ (td->details.policy_details.no_fulfillment_id)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_uint64 (
+ &td->details.policy_details.fulfillment_id),
GNUNET_PQ_query_param_end
};
diff --git a/src/exchangedb/lrbt_callbacks.c b/src/exchangedb/lrbt_callbacks.c
index 5fe0817e..d24dc688 100644
--- a/src/exchangedb/lrbt_callbacks.c
+++ b/src/exchangedb/lrbt_callbacks.c
@@ -1435,33 +1435,47 @@ lrbt_cb_table_policy_details (void *cls,
unsigned int num_results)
{
struct LookupRecordsByTableContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
struct TALER_EXCHANGEDB_TableData td = {
.table = TALER_EXCHANGEDB_RT_POLICY_DETAILS
};
for (unsigned int i = 0; i<num_results; i++)
{
- bool no_config = false;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
&td.serial),
- GNUNET_PQ_result_spec_auto_from_type ("serial_id",
+ GNUNET_PQ_result_spec_auto_from_type ("hash_code",
&td.details.policy_details.
- serial_id),
+ hash_code),
GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_string ("policy_options",
- &td.details.policy_details.
- policy_options),
- &no_config),
+ TALER_PQ_result_spec_json ("policy_json",
+ &td.details.policy_details.
+ policy_json),
+ &td.details.policy_details.no_policy_json),
GNUNET_PQ_result_spec_timestamp ("deadline",
&td.details.policy_details.
deadline),
- GNUNET_PQ_result_spec_uint16 ("timeout_fulfilment_state",
- &td.details.policy_details.
- timeout_fulfilment_state),
- GNUNET_PQ_result_spec_uint16 ("fulfilment_state",
+ TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
+ &td.details.policy_details.
+ commitment),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
+ &td.details.policy_details.
+ accumulated_total),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("fee",
+ &td.details.policy_details.
+ fee),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("transferable",
+ &td.details.policy_details.
+ transferable),
+ GNUNET_PQ_result_spec_uint16 ("fulfillment_state",
&td.details.policy_details.
- fulfilment_state),
+ fulfillment_state),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
+ &td.details.policy_details.
+ fulfillment_id),
+ &td.details.policy_details.no_fulfillment_id),
GNUNET_PQ_result_spec_end
};
@@ -1482,16 +1496,16 @@ lrbt_cb_table_policy_details (void *cls,
/**
- * Function called with policy_fulfilments table entries.
+ * Function called with policy_fulfillments table entries.
*
* @param cls closure
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
-lrbt_cb_table_policy_fulfilments (void *cls,
- PGresult *result,
- unsigned int num_results)
+lrbt_cb_table_policy_fulfillments (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
struct LookupRecordsByTableContext *ctx = cls;
struct TALER_EXCHANGEDB_TableData td = {
@@ -1503,17 +1517,17 @@ lrbt_cb_table_policy_fulfilments (void *cls,
bool no_config = false;
bool no_timestamp = false;
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("fulfilment_id",
+ GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
&td.serial),
GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_timestamp ("fulfilment_timestamp",
- &td.details.policy_fulfilments.
- fulfilment_timestamp),
+ GNUNET_PQ_result_spec_timestamp ("fulfillment_timestamp",
+ &td.details.policy_fulfillments.
+ fulfillment_timestamp),
&no_timestamp),
GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_string ("fulfilment_proof",
- &td.details.policy_fulfilments.
- fulfilment_proof),
+ GNUNET_PQ_result_spec_string ("fulfillment_proof",
+ &td.details.policy_fulfillments.
+ fulfillment_proof),
&no_config),
GNUNET_PQ_result_spec_end
};
@@ -1535,52 +1549,6 @@ lrbt_cb_table_policy_fulfilments (void *cls,
/**
- * Function called with policy_details_fulfilments table entries.
- *
- * @param cls closure
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-lrbt_cb_table_policy_details_fulfilments (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct LookupRecordsByTableContext *ctx = cls;
- struct TALER_EXCHANGEDB_TableData td = {
- .table = TALER_EXCHANGEDB_RT_POLICY_DETAILS_FULFILMENTS
- };
-
- for (unsigned int i = 0; i<num_results; i++)
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("fulfilment_id",
- &td.details.policy_details_fulfilments.
- fulfilment_id),
- GNUNET_PQ_result_spec_auto_from_type ("serial_id",
- &td.details.
- policy_details_fulfilments.
- serial_id),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- ctx->error = true;
- return;
- }
- ctx->cb (ctx->cb_cls,
- &td);
- GNUNET_PQ_cleanup_result (rs);
- }
-}
-
-
-/**
* Function called with purse_requests table entries.
*
* @param cls closure
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 415417cb..062133e6 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -822,8 +822,8 @@ prepare_statements (struct PostgresClosure *pg)
",out_balance_ok AS balance_ok"
",out_conflict AS conflicted"
" FROM exchange_do_deposit"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20);",
- 20),
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);",
+ 17),
/* used in postgres_do_purse_deposit() */
GNUNET_PQ_make_prepare (
"call_purse_deposit",
@@ -3927,37 +3927,45 @@ prepare_statements (struct PostgresClosure *pg)
"($1, $2, $3);",
3),
GNUNET_PQ_make_prepare (
- "insert_into_table_policy_details",
- "INSERT INTO policy_details"
- "(policy_details_serial_id"
- ",serial_id"
- ",policy_options"
- ",deadline"
- ",timeout_fulfilment_state"
- ",fulfilment_state"
- ") VALUES "
- "($1, $2, $3, $4, $5, $6);",
- 6),
- GNUNET_PQ_make_prepare (
- "insert_into_table_policy_fulfilments",
- "INSERT INTO policy_fulfilments"
- "(fulfilment_id"
- ",fulfilment_timestamp"
- ",fulfilment_proof"
+ "call_insert_or_update_policy_details",
+ "SELECT "
+ " out_policy_details_serial_id as policy_serial_id"
+ ",out_accumulated_total_val as accumulated_total_val"
+ ",out_accumulated_total_frac as accumulated_total_frac"
+ ",out_fulfillment_state as fulfillment_state"
+ " FROM insert_or_update_policy_details"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);",
+ 12),
+ GNUNET_PQ_make_prepare (
+ "insert_proof_into_policy_fulfillments",
+ "INSERT INTO policy_fulfillments "
+ "(fulfillment_timestamp "
+ ",fulfillment_proof"
+ ",h_fulfillment_proof"
+ ",policy_hash_codes"
") VALUES "
- "($1, $2, $3);",
- 3),
+ "($1, $2, $3, $4) "
+ "RETURNING fulfillment_id;",
+ 4),
GNUNET_PQ_make_prepare (
- "insert_into_table_policy_details_fulfilments",
- "INSERT INTO policy_details_fulfilments"
- "(fulfilment_id"
- ",serial_id"
- ") VALUES "
- "($1, $2);",
- 2),
+ "update_policy_details",
+ "UPDATE policy_details "
+ "SET "
+ " deadline=$2,"
+ " commitment_val=$3,"
+ " commitment_frac=$4,"
+ " accumulated_total_val=$5,"
+ " accumulated_total_frac=$6,"
+ " fee_val=$7,"
+ " fee_frac=$8,"
+ " transferable_val=$9,"
+ " transferable_frac=$10,"
+ " fulfillment_state=$11 "
+ "WHERE policy_hash_code=$1;",
+ 11),
GNUNET_PQ_make_prepare (
"insert_into_table_purse_requests",
- "INSERT INTO purse_requests"
+ "INSERT INTO purse_requests "
"(purse_requests_serial_id"
",purse_pub"
",merge_pub"
@@ -6250,7 +6258,7 @@ compute_shard (const struct TALER_MerchantPublicKeyP *merchant_pub)
* @param deposit deposit operation details
* @param known_coin_id row of the coin in the known_coins table
* @param h_payto hash of the merchant's bank account details
- * @param policy_blocked true if an extension is blocking the wire transfer
+ * @param policy_details_serial_id (pointer to) the row ID in the policy_details table, maybe NULL.
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] in_conflict set to true if the deposit conflicted
@@ -6262,6 +6270,7 @@ postgres_do_deposit (
const struct TALER_EXCHANGEDB_Deposit *deposit,
uint64_t known_coin_id,
const struct TALER_PaytoHashP *h_payto,
+ uint64_t *policy_details_serial_id,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
bool *in_conflict)
@@ -6283,19 +6292,10 @@ postgres_do_deposit (
GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
GNUNET_PQ_query_param_uint64 (&deposit_shard),
- GNUNET_PQ_query_param_bool (deposit->has_policy_details),
- (deposit->has_policy_details)
- ? TALER_PQ_query_param_json (deposit->policy_details)
- : GNUNET_PQ_query_param_null (),
- (deposit->has_policy_details)
- ? GNUNET_PQ_query_param_auto_from_type (&deposit->policy_serial_id)
- : GNUNET_PQ_query_param_null (),
- (deposit->has_policy_details)
- ? GNUNET_PQ_query_param_timestamp (&deposit->policy_deadline)
- : GNUNET_PQ_query_param_null (),
- (deposit->has_policy_details)
- ? GNUNET_PQ_query_param_uint16 (&deposit->policy_state_on_timeout)
- : GNUNET_PQ_query_param_null (),
+ GNUNET_PQ_query_param_bool (deposit->has_policy),
+ (NULL == policy_details_serial_id)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_uint64 (policy_details_serial_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -6315,6 +6315,101 @@ postgres_do_deposit (
}
+/* Get the details of a policy, referenced by its hash code
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param hc The hash code under which the details to a particular policy should be found
+ * @param[out] details The found details
+ * @return query execution status
+ * */
+static enum GNUNET_DB_QueryStatus
+postgres_get_policy_details (
+ void *cls,
+ const struct GNUNET_HashCode *hc,
+ struct TALER_PolicyDetails *details)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (hc),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_timestamp ("deadline",
+ &details->deadline),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
+ &details->commitment),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
+ &details->accumulated_total),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("policy_fee",
+ &details->policy_fee),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("transferable_amount",
+ &details->transferable_amount),
+ GNUNET_PQ_result_spec_auto_from_type ("state",
+ &details->fulfillment_state),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_uint64 ("policy_fulfillment_id",
+ &details->policy_fulfillment_id),
+ &details->no_policy_fulfillment_id),
+ GNUNET_PQ_result_spec_end
+ };
+
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "get_policy_details",
+ params,
+ rs);
+}
+
+
+/* Persist the details to a policy in the policy_details table. If there
+ * already exists a policy, update the fields accordingly.
+ *
+ * @param details The policy details that should be persisted. If an entry for
+ * the given details->hash_code exists, the values will be updated.
+ * @param[out] policy_details_serial_id The row ID of the policy details
+ * @param[out] accumulated_total The total amount accumulated in that policy
+ * @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready.
+ * @return query execution status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_persist_policy_details (
+ void *cls,
+ const struct TALER_PolicyDetails *details,
+ uint64_t *policy_details_serial_id,
+ struct TALER_Amount *accumulated_total,
+ enum TALER_PolicyFulfillmentState *fulfillment_state)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&details->hash_code),
+ TALER_PQ_query_param_json (details->policy_json),
+ GNUNET_PQ_query_param_timestamp (&details->deadline),
+ TALER_PQ_query_param_amount (&details->commitment),
+ TALER_PQ_query_param_amount (&details->accumulated_total),
+ TALER_PQ_query_param_amount (&details->policy_fee),
+ TALER_PQ_query_param_amount (&details->transferable_amount),
+ GNUNET_PQ_query_param_auto_from_type (&details->fulfillment_state),
+ (details->no_policy_fulfillment_id)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_uint64 (&details->policy_fulfillment_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
+ policy_details_serial_id),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
+ accumulated_total),
+ GNUNET_PQ_result_spec_uint32 ("fulfillment_state",
+ fulfillment_state),
+ GNUNET_PQ_result_spec_end
+ };
+
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "call_insert_or_update_policy_details",
+ params,
+ rs);
+}
+
+
/**
* Perform melt operation, checking for sufficient balance
* of the coin and possibly persisting the melt details.
@@ -6569,6 +6664,118 @@ postgres_do_recoup_refresh (
}
+/*
+ * Compares two indices into an array of hash codes according to
+ * GNUNET_CRYPTO_hash_cmp of the content at those index positions.
+ *
+ * Used in a call qsort_t in order to generate sorted policy_hash_codes.
+ */
+static int
+hash_code_cmp (
+ const void *hc1,
+ const void *hc2,
+ void *arg)
+{
+ size_t i1 = *(size_t *) hc1;
+ size_t i2 = *(size_t *) hc2;
+ const struct TALER_PolicyDetails *d = arg;
+
+ return GNUNET_CRYPTO_hash_cmp (&d[i1].hash_code,
+ &d[i2].hash_code);
+}
+
+
+/**
+ * Add a proof of fulfillment into the policy_fulfillments table
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param[out] proof_id set record id for the proof
+ * @return query execution status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_add_policy_fulfillment_proof (
+ void *cls,
+ struct TALER_PolicyFulfillmentTransactionData *fulfillment)
+{
+ enum GNUNET_DB_QueryStatus qs;
+ struct PostgresClosure *pg = cls;
+ size_t count = fulfillment->details_count;
+ struct GNUNET_HashCode hcs[count];
+
+ /* Create the sorted policy_hash_codes */
+ {
+ size_t idx[count];
+ for (size_t i = 0; i < count; i++)
+ idx[i] = i;
+
+ /* Sort the indices according to the hash codes of the corresponding
+ * details. */
+ qsort_r (idx,
+ count,
+ sizeof(size_t),
+ hash_code_cmp,
+ fulfillment->details);
+
+ /* Finally, concatenate all hash_codes in sorted order */
+ for (size_t i = 0; i < count; i++)
+ hcs[i] = fulfillment->details[idx[i]].hash_code;
+ }
+
+
+ /* Now, add the proof to the policy_fulfillments table, retrieve the
+ * record_id */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_timestamp (&fulfillment->timestamp),
+ TALER_PQ_query_param_json (fulfillment->proof),
+ GNUNET_PQ_query_param_auto_from_type (&fulfillment->h_proof),
+ GNUNET_PQ_query_param_fixed_size (hcs,
+ count * sizeof(struct GNUNET_HashCode)),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
+ &fulfillment->fulfillment_id),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "insert_proof_into_policy_fulfillments",
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ return qs;
+ }
+
+ /* Now, set the states of each entry corresponding to the hash_codes in
+ * policy_details accordingly */
+ for (size_t i = 0; i < count; i++)
+ {
+ struct TALER_PolicyDetails *pos = &fulfillment->details[i];
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&pos->hash_code),
+ GNUNET_PQ_query_param_timestamp (&pos->deadline),
+ TALER_PQ_query_param_amount (&pos->commitment),
+ TALER_PQ_query_param_amount (&pos->accumulated_total),
+ TALER_PQ_query_param_amount (&pos->policy_fee),
+ TALER_PQ_query_param_amount (&pos->transferable_amount),
+ GNUNET_PQ_query_param_auto_from_type (&pos->fulfillment_state),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_policy_details",
+ params);
+ if (qs < 0)
+ return qs;
+ }
+ }
+
+ return qs;
+}
+
+
/**
* Closure for callbacks invoked via #postgres_get_reserve_history.
*/
@@ -14470,8 +14677,8 @@ postgres_lookup_records_by_table (void *cls,
rh = &lrbt_cb_table_policy_details;
break;
case TALER_EXCHANGEDB_RT_POLICY_FULFILMENTS:
- statement = "select_above_serial_by_table_policy_fulfilments";
- rh = &lrbt_cb_table_policy_fulfilments;
+ statement = "select_above_serial_by_table_policy_fulfillments";
+ rh = &lrbt_cb_table_policy_fulfillments;
break;
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
statement = "select_above_serial_by_table_purse_requests";
@@ -14655,7 +14862,7 @@ postgres_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
rh = &irbt_cb_table_policy_details;
break;
- /* TODO: policy_details_fulfilments and policy_fulfilments */
+ /* TODO: policy_details_fulfillments and policy_fulfillments */
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
rh = &irbt_cb_table_purse_requests;
break;
@@ -17110,7 +17317,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->do_withdraw = &postgres_do_withdraw;
plugin->do_batch_withdraw = &postgres_do_batch_withdraw;
plugin->do_batch_withdraw_insert = &postgres_do_batch_withdraw_insert;
+ plugin->get_policy_details = &postgres_get_policy_details;
+ plugin->persist_policy_details = &postgres_persist_policy_details;
plugin->do_deposit = &postgres_do_deposit;
+ plugin->add_policy_fulfillment_proof = &postgres_add_policy_fulfillment_proof;
plugin->do_melt = &postgres_do_melt;
plugin->do_refund = &postgres_do_refund;
plugin->do_recoup = &postgres_do_recoup;
diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql
index aeed5de0..85accfc2 100644
--- a/src/exchangedb/procedures.sql
+++ b/src/exchangedb/procedures.sql
@@ -511,10 +511,7 @@ CREATE OR REPLACE FUNCTION exchange_do_deposit(
IN in_coin_sig BYTEA,
IN in_shard INT8,
IN in_policy_blocked BOOLEAN,
- IN in_policy_details VARCHAR,
- IN in_policy_serial_id BYTEA,
- IN in_policy_deadline SMALLINT,
- IN in_policy_timeout_fulfilment_state SMALLINT,
+ IN in_policy_details_serial_id INT8,
OUT out_exchange_timestamp INT8,
OUT out_balance_ok BOOLEAN,
OUT out_conflict BOOLEAN)
@@ -522,34 +519,11 @@ LANGUAGE plpgsql
AS $$
DECLARE
wtsi INT8; -- wire target serial id
-DECLARE
- xdi INT8; -- eXstension details serial id
BEGIN
--- Shards: INSERT policy_details (by policy_details_serial_id)
--- INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING;
+-- Shards: INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING;
-- INSERT deposits (by coin_pub, shard), ON CONFLICT DO NOTHING;
-- UPDATE known_coins (by coin_pub)
-IF NOT NULL in_policy_details
-THEN
- INSERT INTO exchange.policy_details
- (serial_id
- ,policy_options
- ,deadline
- ,timeout_fulfilment_state
- ,fulfilment_state)
- VALUES
- (in_policy_serial_id
- ,in_policy_details
- ,in_policy_deadline
- ,in_policy_timeout_fulfilment_state
- ,0) -- 0 == pending
- RETURNING policy_details_serial_id INTO xdi;
-ELSE
- xdi=NULL;
-END IF;
-
-
INSERT INTO exchange.wire_targets
(wire_target_h_payto
,payto_uri)
@@ -602,7 +576,8 @@ INSERT INTO exchange.deposits
,in_wire_salt
,in_h_payto
,in_policy_blocked
- ,xdi)
+ ,in_policy_details_serial_id
+ )
ON CONFLICT DO NOTHING;
IF NOT FOUND
@@ -622,6 +597,7 @@ THEN
AND wire_target_h_payto=in_h_payto
AND coin_pub=in_coin_pub
AND coin_sig=in_coin_sig;
+ -- AND policy_details_serial_id=in_policy_details_serial_id; -- FIXME: is this required for idempotency?
IF NOT FOUND
THEN
@@ -2211,5 +2187,123 @@ BEGIN
END $$;
+CREATE OR REPLACE FUNCTION insert_or_update_policy_details(
+ IN in_policy_hash_code BYTEA,
+ IN in_policy_json VARCHAR,
+ IN in_deadline INT8,
+ IN in_commitment_val INT8,
+ IN in_commitment_frac INT4,
+ IN in_accumulated_total_val INT8,
+ IN in_accumulated_total_frac INT4,
+ IN in_fee_val INT8,
+ IN in_fee_frac INT4,
+ IN in_transferable_val INT8,
+ IN in_transferable_frac INT4,
+ IN in_fulfillment_state SMALLINT,
+ OUT out_policy_details_serial_id INT8,
+ OUT out_accumulated_total_val INT8,
+ OUT out_accumulated_total_frac INT4,
+ OUT out_fulfillment_state SMALLINT)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ cur_commiment_val INT8;
+ cur_commiment_frac INT4;
+ cur_accumulated_total_val INT8;
+ cur_accumulated_total_frac INT4;
+BEGIN
+ -- First, try to create a new entry.
+ INSERT INTO policy_details
+ (policy_hash_code,
+ policy_json,
+ deadline,
+ commitment_val,
+ commitment_frac,
+ accumulated_total_val,
+ accumulated_total_frac,
+ fee_val,
+ fee_frac,
+ transferable_val,
+ transferable_frac,
+ fulfillment_state)
+ VALUES (in_policy_hash_code,
+ in_policy_json,
+ in_deadline,
+ in_commitment_val,
+ in_commitment_frac,
+ in_accumulated_total_val,
+ in_accumulated_total_frac,
+ in_fee_val,
+ in_fee_frac,
+ in_transferable_val,
+ in_transferable_frac,
+ in_fulfillment_state)
+ RETURNING (policy_details_serial_id,
+ accumulated_total_val,
+ accumulated_total_frac,
+ fulfillment_state)
+ INTO (out_policy_details_serial_id,
+ out_accumulated_total_val,
+ out_accumulated_total_frac,
+ out_fulfillment_state)
+ ON CONFLICT (policy_hash_code) DO NOTHING;
+
+ -- If the insert was successful, return
+ -- We assume that the fullfilment_state was correct in first place.
+ IF FOUND THEN
+ RETURN;
+ END IF;
+
+ -- We had a conflict, grab the parts we need to update.
+ SELECT (policy_details_serial_id,
+ commitment_val,
+ commitment_frac,
+ accumulated_total_val,
+ accumulated_total_frac)
+ INTO (out_policy_details_serial_id,
+ cur_commitment_val,
+ cur_commitment_frac,
+ cur_accumulated_total_val,
+ cur_accumulated_total_frac)
+ FROM policy_details
+ WHERE policy_hash_code = in_policy_hash_code;
+
+ -- calculate the new values (overflows throws exception)
+ out_accumulated_total_val = cur_accumulated_total_val + in_accumulated_total_val;
+ out_accumulated_total_frac = cur_accumulated_total_frac + in_accumulated_total_frac;
+ -- normalize
+ out_accumulated_total_val = out_accumulated_total_val + out_accumulated_total_frac / 100000000;
+ out_accumulated_total_frac = out_accumulated_total_frac % 100000000;
+
+ IF (out_accumulated_total_val > (1 << 52))
+ THEN
+ RAISE EXCEPTION "accumulation overflow";
+ END IF;
+
+
+ -- Set the fulfillment_state according to the values.
+ -- For now, we only update the state when it was INSUFFICIENT.
+ -- FIXME: What to do in case of Failure or other state?
+ IF (out_fullfillment_state = 1) -- INSUFFICIENT
+ THEN
+ IF (out_accumulated_total_val >= cur_commitment_val OR
+ (out_accumulated_total_val = cur_commitment_val AND
+ out_accumulated_total_frac >= cur_commitment_frac))
+ THEN
+ out_fulfillment_state = 2; -- READY
+ END IF;
+ END IF;
+
+ -- Now, update the record
+ UPDATE exchange.policy_details
+ SET
+ accumulated_val = out_accumulated_total_val,
+ accumulated_frac = out_accumulated_total_frac,
+ fulfillment_state = out_fulfillment_state
+ WHERE
+ policy_details_serial_id = out_policy_details_serial_id;
+END $$;
+
+
COMMIT;
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 6acf0136..e0ad23b2 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1500,6 +1500,7 @@ run (void *cls)
&deposit,
known_coin_id,
&h_payto,
+ NULL, /* no policy_details_serial_id */
&deposit_timestamp,
&balance_ok,
&in_conflict));
diff --git a/src/extensions/age_restriction/age_restriction.c b/src/extensions/age_restriction/age_restriction.c
index 3c38d0f8..58124250 100644
--- a/src/extensions/age_restriction/age_restriction.c
+++ b/src/extensions/age_restriction/age_restriction.c
@@ -153,7 +153,7 @@ struct TALER_Extension TE_age_restriction = {
.manifest = &age_restriction_manifest,
/* This extension is not a policy extension */
- .parse_policy_details = NULL,
+ .create_policy_details = NULL,
.policy_get_handler = NULL,
.policy_post_handler = NULL,
};
diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c
index cfc10940..2ed973d9 100644
--- a/src/extensions/extensions.c
+++ b/src/extensions/extensions.c
@@ -357,31 +357,27 @@ TALER_extensions_load_manifests (
* Policy related
*/
-static char *fulfilment2str[] = {
- [TALER_PolicyFulfilmentPending] = "Pending",
- [TALER_PolicyFulfilmentSuccessTransfer] = "SuccessTransfer",
- [TALER_PolicyFulfilmentSuccessRefreshable] = "SuccessRefreshable",
- [TALER_PolicyFulfilmentFailureTransfer] = "FailureTransfer",
- [TALER_PolicyFulfilmentFailureRefreshable] = "FailureRefreshable",
- [TALER_PolicyFulfilmentTimeoutTransfer] = "TimeoutTransfer",
- [TALER_PolicyFulfilmentTimeoutRefreshable] = "TimeoutRefreshable",
+static char *fulfillment2str[] = {
+ [TALER_PolicyFulfillmentReady] = "Ready",
+ [TALER_PolicyFulfillmentSuccess] = "Success",
+ [TALER_PolicyFulfillmentFailure] = "Failure",
+ [TALER_PolicyFulfillmentTimeout] = "Timeout",
+ [TALER_PolicyFulfillmentInsufficient] = "Insufficient",
};
const char *
-TALER_policy_fulfilment_state_str (
- enum TALER_PolicyFulfilmentState state)
+TALER_policy_fulfillment_state_str (
+ enum TALER_PolicyFulfillmentState state)
{
- GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state);
- return fulfilment2str[state];
+ GNUNET_assert (TALER_PolicyFulfillmentStateCount > state);
+ return fulfillment2str[state];
}
enum GNUNET_GenericReturnValue
-TALER_extensions_extract_meta_data_from_policy_details (
- const json_t *policy_details,
- struct GNUNET_HashCode *serial,
- struct GNUNET_TIME_Timestamp *deadline,
- enum TALER_PolicyFulfilmentState *state_on_deadline,
+TALER_extensions_create_policy_details (
+ const json_t *policy_options,
+ struct TALER_PolicyDetails *details,
const char **error_hint)
{
enum GNUNET_GenericReturnValue ret;
@@ -391,14 +387,14 @@ TALER_extensions_extract_meta_data_from_policy_details (
*error_hint = NULL;
- if ((NULL == policy_details) ||
- (! json_is_object (policy_details)))
+ if ((NULL == policy_options) ||
+ (! json_is_object (policy_options)))
{
*error_hint = "invalid policy object";
return GNUNET_SYSERR;
}
- jtype = json_object_get (policy_details, "type");
+ jtype = json_object_get (policy_options, "type");
if (NULL == jtype)
{
*error_hint = "no type in policy object";
@@ -414,7 +410,7 @@ TALER_extensions_extract_meta_data_from_policy_details (
extension = TALER_extensions_get_by_name (type);
if ((NULL == extension) ||
- (NULL == extension->parse_policy_details))
+ (NULL == extension->create_policy_details))
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -423,50 +419,13 @@ TALER_extensions_extract_meta_data_from_policy_details (
return GNUNET_NO;
}
- *deadline = GNUNET_TIME_UNIT_FOREVER_TS;
- ret = extension->parse_policy_details (policy_details,
- serial,
- deadline,
- state_on_deadline,
- error_hint);
-
- GNUNET_assert ((TALER_PolicyFulfilmentTimeoutRefreshable ==
- *state_on_deadline) ||
- (TALER_PolicyFulfilmentTimeoutTransfer ==
- *state_on_deadline));
-
+ details->deadline = GNUNET_TIME_UNIT_FOREVER_TS;
+ ret = extension->create_policy_details (policy_options,
+ details,
+ error_hint);
return ret;
}
-struct TALER_PolicyFulfilmentOutcome *
-TALER_policy_fulfilment_outcome_new (size_t len)
-{
- struct TALER_PolicyFulfilmentOutcome *out;
-
- out = GNUNET_new (struct TALER_PolicyFulfilmentOutcome);
- out->len = len;
- out->positions = GNUNET_new_array (len,
- struct
- TALER_PolicyFulfilmentOutcomePosition);
-
- return out;
-}
-
-
-void
-TALER_policy_fulfilment_outcome_free (
- struct TALER_PolicyFulfilmentOutcome *outcome)
-{
- if (NULL == outcome)
- return;
-
- if (NULL != outcome->positions)
- GNUNET_free (outcome->positions);
-
- GNUNET_free (outcome);
-}
-
-
/* end of extensions.c */
diff --git a/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c b/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c
index bfd7b813..34c84bb4 100644
--- a/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c
+++ b/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c
@@ -31,7 +31,7 @@
#define MAX_RESULT_SIZE 10 * 1024
/* (public) configuration of this extension */
-/* TODO: these fields need to be set in the init handler */
+/* FIXME: these fields need to be set in the init handler */
static struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig BV_config = {
.max_bidders = 10,
.max_prices = 10,
@@ -82,8 +82,8 @@ struct transcript
/* Hash of the auction */
struct GNUNET_HashCode h_auction;
- /* (n-1) calculated serial_ids */
- struct GNUNET_HashCode *serial_ids;
+ /* (n-1) calculated hash_codes */
+ struct GNUNET_HashCode *hash_codes;
/* Type of auction, see libbrandt */
@@ -146,7 +146,7 @@ json_error (json_t **output,
/* Create serial as H(bidder_pub, h_auction) */
static void
-calculate_serial (
+calculate_hashcode (
struct GNUNET_CRYPTO_EddsaPublicKey *pub,
struct GNUNET_HashCode *hc,
struct GNUNET_HashCode *serial)
@@ -171,7 +171,7 @@ calculate_serial (
* @param[out] jerror JSON output for errors
* @return GNUNET_OK on succes
*
- * TODO:
+ * FIXME:
* - parse and verify signatures
*/
static enum GNUNET_GenericReturnValue
@@ -181,7 +181,7 @@ parse_transcript (const json_t *jtr,
{
json_t *auc;
- // TODO: struct GNUNET_CRYPTO_EddsaSignature sig;
+ // FIXME: struct GNUNET_CRYPTO_EddsaSignature sig;
GNUNET_assert (jtr);
GNUNET_assert (tr);
@@ -272,7 +272,7 @@ parse_transcript (const json_t *jtr,
tr->n,
struct GNUNET_CRYPTO_EddsaPublicKey);
- tr->serial_ids = GNUNET_new_array (
+ tr->hash_codes = GNUNET_new_array (
tr->n,
struct GNUNET_HashCode);
@@ -284,7 +284,7 @@ parse_transcript (const json_t *jtr,
GNUNET_JSON_spec_end (),
};
- /* TODO: cleanup */
+ /* FIXME: cleanup */
if (GNUNET_OK !=
GNUNET_JSON_parse (val,
spec,
@@ -294,9 +294,9 @@ parse_transcript (const json_t *jtr,
"bidder no %ld public key couldn't be parsed",
idx + 1);
- calculate_serial (&tr->bidder_pub[idx],
- &tr->h_auction,
- &tr->serial_ids[idx]);
+ calculate_hashcode (&tr->bidder_pub[idx],
+ &tr->h_auction,
+ &tr->hash_codes[idx]);
}
}
@@ -316,7 +316,7 @@ parse_transcript (const json_t *jtr,
return json_error (jerror,
"not the right no. of messages found");
- /* TODO: parse and evaluate signatures */
+ /* FIXME: parse and evaluate signatures */
}
// Winners
@@ -358,9 +358,9 @@ parse_transcript (const json_t *jtr,
}
}
- // TODO: parse and evalue sig of seller
+ // FIXME: parse and evalue sig of seller
-// TODO: check for max values
+// FIXME: check for max values
DONE:
@@ -373,16 +373,18 @@ DONE:
*
* @param[in] root The original JSON transcript
* @param[in,out] transcript The transcript object parsed so far
- * @param[out] outcome Outcome object to fill
+ * @param[in/out] details Array of policy details, provided by the exchange
+ * @param[in] details_count number of elements in @e details
* @param[out] result The JSON result from the program
* @return GNUNET_OK on success
*
- * TODO: Make this resumable
+ * FIXME: Make this resumable
*/
static enum GNUNET_GenericReturnValue
replay_transcript (const json_t*root,
struct transcript *tr,
- struct TALER_PolicyFulfilmentOutcome **outcome,
+ struct TALER_PolicyDetails *details,
+ size_t details_count,
json_t **result)
{
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
@@ -488,30 +490,33 @@ replay_transcript (const json_t*root,
return json_error (result, "internal error");
}
- // TODO: check each winner with tr->expected, if applicable
+ // FIXME: check each winner with tr->expected, if applicable
- // Create the outcome object
+ /* First, set all details to default state and values */
+ for (size_t i = 0; i< tr->n; i++)
{
- json_t *w;
- size_t idx;
- struct TALER_PolicyFulfilmentOutcomePosition *pos;
+ /* no fees for non-winners */
+ TALER_amount_set_zero (details[i].policy_fee.currency,
+ &details[i].policy_fee);
- *outcome =
- TALER_policy_fulfilment_outcome_new (tr->n);
+ /* no transferable amounts (=> accumulated_total becomes refreshable) */
+ TALER_amount_set_zero (details[i].transferable_amount.currency,
+ &details[i].transferable_amount);
+
+ details[i].fulfillment_state = TALER_PolicyFulfillmentSuccess;
+ }
- /* Set outcome for all bidders to a default value first */
- for (uint16_t i = 0; i<tr->n; i++)
- {
- pos = &((*outcome)->positions[i]);
- pos->serial_id = tr->serial_ids[i];
- pos->has_new_amount = false;
- pos->state = TALER_PolicyFulfilmentFailureRefreshable;
- }
+
+ /* Now, fill the winner details */
+ {
+ json_t *w;
+ size_t idx;
/* Set the outcome of the winners */
json_array_foreach (winners, idx, w)
{
+ // TODO
enum GNUNET_GenericReturnValue ret;
const char *jerror;
uint16_t bidder;
@@ -540,7 +545,7 @@ replay_transcript (const json_t*root,
LOG_PREFIX
"couldn't parse output of replay program: %s\n",
jerror);
- ret = json_error (result, "internal error");
+ ret = json_error (result, "internal error (replay)");
goto DONE;
}
@@ -549,19 +554,41 @@ replay_transcript (const json_t*root,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
LOG_PREFIX
"replay program sent a bidder out of range\n");
- ret = json_error (result, "internal error");
+ ret = json_error (result, "internal error (bidder)");
goto DONE;
}
- // Fill the outcome position for this winning bidder.
- pos = &((*outcome)->positions[idx]);
- pos->has_new_amount = true;
- pos->new_amount = price;
- pos->state = TALER_PolicyFulfilmentSuccessTransfer;
+ // Fill the details for this winning bidder.
+ {
+ struct TALER_PolicyDetails *det = NULL;
+ struct GNUNET_HashCode *hc = &tr->hash_codes[idx];
+
+ /* Find the corresponding details */
+ for (size_t i = 0; i < tr->n; i++)
+ {
+ if (GNUNET_CRYPTO_hash_cmp (hc,
+ &details[idx].hash_code))
+ {
+ det = &details[idx];
+ break;
+ }
+ }
+
+ if (NULL == det)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ LOG_PREFIX
+ "Couldeln't find calculate hash code %s in details\n",
+ GNUNET_h2s (hc));
+ ret = json_error (result, "internal error (details)");
+ goto DONE;
+ }
+
+ }
}
- // TODO: return own result object.
- // TODO: sign the result by the exchange
+ // FIXME: return own result object.
+ // FIXME: sign the result by the exchange
*result = json_copy (res);
json_decref (res);
}
@@ -582,11 +609,6 @@ replay_transcript (const json_t*root,
}
DONE:
- if (GNUNET_OK != ret)
- {
- TALER_policy_fulfilment_outcome_free (*outcome);
- *outcome = NULL;
- }
return ret;
}
@@ -642,7 +664,7 @@ auction_load_config (
struct TALER_Extension *ext,
json_t *jconfig)
{
- /* TODO: parse configuration */
+ /* FIXME: parse configuration */
ext->enabled = true;
return GNUNET_OK;
}
@@ -656,7 +678,7 @@ auction_policy_get_handler (
struct MHD_Connection *connection,
const char *const args[])
{
- /* TODO: return some meta-data about supported version, limits, etc.*/
+ /* FIXME: return some meta-data about supported version, limits, etc.*/
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
LOG_PREFIX "auction_policy_get_handler not implemented yet\n");
@@ -672,55 +694,89 @@ auction_policy_get_handler (
/**
* @brief implements the TALER_Extension.policy_post_handler
*
- * TODO: make this non-blocking
+ * FIXME: make this non-blocking
*/
enum GNUNET_GenericReturnValue
auction_policy_post_handler (
const json_t *root,
const char *const args[],
- enum GNUNET_GenericReturnValue (*serial_id_check)(struct GNUNET_HashCode
- serial_ids[], size_t size),
- struct TALER_PolicyFulfilmentOutcome **outcome,
+ struct TALER_PolicyDetails *details,
+ size_t details_len,
json_t **output)
{
- struct transcript tr = {};
+ struct transcript tr = {0};
enum GNUNET_GenericReturnValue ret;
- *outcome = NULL;
- ret = parse_transcript (root,
- &tr,
- output);
+ do {
+ ret = parse_transcript (root,
+ &tr,
+ output);
- /* TODO: cleanups! */
- if (GNUNET_OK != ret)
- return ret;
+ if (GNUNET_OK != ret)
+ break;
- serial_id_check (tr.serial_ids, tr.n);
+ /* Compare the calculated hash_codes of policies with the provided onces */
+ {
+ if (details_len != tr.n)
+ {
+ ret = json_error (output, "wrong number of bidders");
+ break;
+ }
- return replay_transcript (root,
- &tr,
- outcome,
- output);
+ for (size_t i = 0; i<details_len; i++)
+ {
+ bool found = false;
+ for (size_t j = 0; j<tr.n; j++)
+ {
+ if (GNUNET_CRYPTO_hash_cmp (&details[i].hash_code,
+ &tr.hash_codes[j]))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (! found)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "details[%ld].hash_code %s not found in transcript\n",
+ i,
+ GNUNET_h2s (&details[i].hash_code));
+ ret = json_error (output, "internal error (hash_code)");
+ goto END;
+ }
+ }
+ }
+
+ ret = replay_transcript (root,
+ &tr,
+ details,
+ details_len,
+ output);
+ } while(0);
+
+END:
+ GNUNET_free (tr.prices);
+ GNUNET_free (tr.bidder_pub);
+ GNUNET_free (tr.hash_codes);
+ GNUNET_free (tr.expected);
+
+ return ret;
}
/**
- * @brief implements the TALER_Extensions.parse_policy_details interface.
+ * @brief implements the TALER_Extensions.create_policy_details interface.
*
* @param[in] input The policy_details for this handler during deposit
- * @param[out] serial On success will contain the serial-ID under which the
- * @param[out] deadline On success will contain a deadline, might be "forever"
- * @param[out] state_on_timeout On success, will be set to the default state that the policy shall be put in case of a timeout.
- * @param[out] error_hint On error, will contain a hint
- * exchange should store the policy_details in the policy_details table.
+ * @param[out] details On success will contain the details to the policy
+ * @param[out] error_hint On error, will contain a hint exchange should store
+ * the policy_details in the policy_details table.
* @return GNUNET_OK if the request was OK
*/
enum GNUNET_GenericReturnValue
-auction_parse_policy_details (
+auction_create_policy_details (
const json_t *input,
- struct GNUNET_HashCode *serial,
- struct GNUNET_TIME_Timestamp *deadline,
- enum TALER_PolicyFulfilmentState *state_on_timeout,
+ struct TALER_PolicyDetails *details,
const char **error_hint)
{
enum GNUNET_GenericReturnValue ret = GNUNET_NO;
@@ -730,13 +786,15 @@ auction_parse_policy_details (
/* We ignore the "type" field as it must have been parsed already upstream
* - or this handler wouldn't have been called in first place. */
GNUNET_JSON_spec_fixed_auto ("bidder_pub", &pub),
+ TALER_JSON_spec_amount ("commitment",
+ BV_config.auction_fee.currency,
+ &details->commitment),
GNUNET_JSON_spec_fixed_auto ("h_auction", &hc),
- GNUNET_JSON_spec_timestamp ("deadline", deadline),
+ GNUNET_JSON_spec_timestamp ("deadline", &details->deadline),
GNUNET_JSON_spec_end (),
};
- GNUNET_assert (serial);
- GNUNET_assert (deadline);
+ GNUNET_assert (details);
error_hint = NULL;
@@ -752,16 +810,13 @@ auction_parse_policy_details (
/* FIXME: check the deadline */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
LOG_PREFIX "check of deadline %ld not implemented!\n",
- deadline->abs_time.abs_value_us);
+ details->deadline.abs_time.abs_value_us);
- calculate_serial (&pub, &hc, serial);
+ calculate_hashcode (&pub, &hc, &details->hash_code);
ret = GNUNET_OK;
} while(0);
- /* In case of timeout, the coin shall be refreshable by the owner */
- *state_on_timeout = TALER_PolicyFulfilmentTimeoutRefreshable;
-
return ret;
}
@@ -777,7 +832,7 @@ struct TALER_Extension TE_auction_brandt = {
.disable = &auction_disable,
.load_config = &auction_load_config,
.manifest = &auction_manifest,
- .parse_policy_details = &auction_parse_policy_details,
+ .create_policy_details = &auction_create_policy_details,
.policy_get_handler = &auction_policy_get_handler,
.policy_post_handler = &auction_policy_post_handler,
};
@@ -847,7 +902,7 @@ libtaler_extension_policy_brandt_vickrey_auction_init (void *arg)
LOG_PREFIX "loading... using replay_program '%s'\n",
replay_program);
- /* TODO: read other config parameters and generate configuration */
+ /* FIXME: read other config parameters and generate configuration */
return &TE_auction_brandt;
}
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 80a19b67..881e0cef 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -26,6 +26,7 @@
#include <gnunet/gnunet_db_lib.h>
#include "taler_json_lib.h"
#include "taler_signatures.h"
+#include "taler_extensions_policy.h"
/**
@@ -223,7 +224,6 @@ enum TALER_EXCHANGEDB_ReplicatedTable
TALER_EXCHANGEDB_RT_EXTENSIONS,
TALER_EXCHANGEDB_RT_POLICY_DETAILS,
TALER_EXCHANGEDB_RT_POLICY_FULFILMENTS,
- TALER_EXCHANGEDB_RT_POLICY_DETAILS_FULFILMENTS,
TALER_EXCHANGEDB_RT_PURSE_REQUESTS,
TALER_EXCHANGEDB_RT_PURSE_REFUNDS,
TALER_EXCHANGEDB_RT_PURSE_MERGES,
@@ -528,24 +528,27 @@ struct TALER_EXCHANGEDB_TableData
struct
{
- char *policy_options;
- struct GNUNET_HashCode serial_id;
+ struct GNUNET_HashCode hash_code;
+ json_t *policy_json;
+ bool no_policy_json;
struct GNUNET_TIME_Timestamp deadline;
- uint16_t timeout_fulfilment_state;
- uint16_t fulfilment_state;
+ struct TALER_Amount commitment;
+ struct TALER_Amount accumulated_total;
+ struct TALER_Amount fee;
+ struct TALER_Amount transferable;
+ uint16_t fulfillment_state; /* will also be recomputed */
+ uint64_t fulfillment_id;
+ bool no_fulfillment_id;
} policy_details;
struct
{
- struct GNUNET_HashCode serial_id;
- uint64_t fulfilment_id;
- } policy_details_fulfilments;
-
- struct
- {
- char *fulfilment_proof;
- struct GNUNET_TIME_Timestamp fulfilment_timestamp;
- } policy_fulfilments;
+ struct GNUNET_TIME_Timestamp fulfillment_timestamp;
+ char *fulfillment_proof;
+ struct GNUNET_HashCode h_fulfillment_proof;
+ struct GNUNET_HashCode *policy_hash_codes;
+ size_t policy_hash_codes_count;
+ } policy_fulfillments;
struct
{
@@ -889,7 +892,6 @@ struct TALER_EXCHANGEDB_DenominationKeyMetaData
struct TALER_AgeMask age_mask;
};
-
/**
* Signature of a function called with information about the exchange's
* denomination keys.
@@ -1446,36 +1448,28 @@ struct TALER_EXCHANGEDB_Deposit
char *receiver_wire_account;
/**
- * Additional details for a policy relevant for this
- * deposit operation, possibly NULL!
+ * Additional policy and its options, relevant for this deposit operation,
+ * possibly NULL!
*/
- json_t *policy_details;
- bool has_policy_details;
+ json_t *policy_json;
- /**
- * If policy_details are present, the corresponding policy extension
- * calculates a serial id under which the policy_details shall be stored in
- * the policy_details table.
+ /*
+ * True if @e policy_json was provided
*/
- struct GNUNET_HashCode policy_serial_id;
+ bool has_policy;
/**
- * If policy_details are present, the corresponding policy extension can set
- * a deadline for this policy. Can be "forever".
+ * Hash over the @e policy_options. Only filled if @e has_policy is true.
+ * Needed for the verification of the deposit's signature
*/
- struct GNUNET_TIME_Timestamp policy_deadline;
-
- /**
- * The state that a _pending_ policy should be put into once the timeout triggers
- */
- uint16_t policy_state_on_timeout;
-
+ struct TALER_ExtensionPolicyHashP h_policy;
/**
- * Hash over the @e policy_details. Only filled if has_policy_details is
- * true.
+ * If @e policy_json was present, the corresponding policy extension
+ * calculates these details. These will be persisted in the policy_details
+ * table.
*/
- struct TALER_ExtensionPolicyHashP h_policy;
+ struct TALER_PolicyDetails policy_details;
/**
* Time when this request was generated. Used, for example, to
@@ -1549,8 +1543,8 @@ struct TALER_EXCHANGEDB_DepositListEntry
struct TALER_PrivateContractHashP h_contract_terms;
/**
- * Hash over the poliy data for this deposit
- * (remains unknown to the Exchange).
+ * Hash over the policy data for this deposit (remains unknown to the
+ * Exchange). Needed for the verification of the deposit's signature
*/
struct TALER_ExtensionPolicyHashP h_policy;
@@ -3342,6 +3336,39 @@ struct TALER_EXCHANGEDB_Plugin
bool *conflict,
bool *nonce_reuse);
+ /**
+ * Retrieve the details to a policy given by its hash_code
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param hc Hash code that identifies the policy
+ * @param[out] detail retrieved policy details
+ * @return query execution status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*get_policy_details)(
+ void *cls,
+ const struct GNUNET_HashCode *hc,
+ struct TALER_PolicyDetails *detail);
+
+ /**
+ * Persist the policy details that extends a deposit. The particular policy
+ * - referenced by details->hash_code - might already exist in the table, in
+ * which case the call will update the contents of the record with @e details
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param details The parsed `struct TALER_PolicyDetails` according to the responsible policy extension.
+ * @param[out] policy_details_serial_id The ID of the entry in the policy_details table
+ * @param[out] accumulated_total The total amount accumulated in that policy
+ * @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready.
+ * @return query execution status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*persist_policy_details)(
+ void *cls,
+ const struct TALER_PolicyDetails *details,
+ uint64_t *policy_details_serial_id,
+ struct TALER_Amount *accumulated_total,
+ enum TALER_PolicyFulfillmentState *fulfillment_state);
/**
* Perform deposit operation, checking for sufficient balance
@@ -3351,6 +3378,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param deposit deposit operation details
* @param known_coin_id row of the coin in the known_coins table
* @param h_payto hash of the merchant's payto URI
+ * @param policy_details_serial_id (pointer to) the row ID of the policy details, maybe NULL
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] in_conflict set to true if the deposit conflicted
@@ -3362,6 +3390,7 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_EXCHANGEDB_Deposit *deposit,
uint64_t known_coin_id,
const struct TALER_PaytoHashP *h_payto,
+ uint64_t *policy_details_serial_id,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
bool *in_conflict);
@@ -3389,6 +3418,18 @@ struct TALER_EXCHANGEDB_Plugin
bool *zombie_required,
bool *balance_ok);
+ /**
+ * Add a proof of fulfillment of an policy
+ *
+ * @param cls the plugin-specific state
+ * @param[in,out] fulfillment The proof of fulfillment and serial_ids of the policy_details along with their new state and potential new amounts.
+ * @return query execution status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*add_policy_fulfillment_proof)(
+ void *cls,
+ struct TALER_PolicyFulfillmentTransactionData *fulfillment);
+
/**
* Check if the given @a nonce was properly locked to the given @a old_coin_pub. If so, check if we already
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 5a5841e5..24e0366d 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -25,7 +25,7 @@
#include "taler_crypto_lib.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
-#include "taler_exchangedb_plugin.h"
+#include "taler_extensions_policy.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
@@ -54,8 +54,8 @@ struct TALER_Extensions
};
/* Forward declarations */
-enum TALER_PolicyFulfilmentState;
-struct TALER_PolicyFulfilmentOutcome;
+enum TALER_PolicyFulfillmentState;
+struct TALER_PolicyFulfillmentOutcome;
/*
* @brief Represents the implementation of an extension.
@@ -165,31 +165,23 @@ struct TALER_Extension
*/
/**
- * @brief Handler to check a policy. Can be NULL;
+ * @brief Handler to check an incoming policy and create a
+ * TALER_PolicyDetails. Can be NULL;
*
* When a deposit request refers to this extension in its policy
* (see https://docs.taler.net/core/api-exchange.html#deposit), this handler
* will be called before the deposit transaction.
*
- * @param[in] policy_details Details about the policy, provided by the client
+ * @param[in] policy_json Details about the policy, provided by the client
* during a deposit request.
- * @param[out] serial On success, will contain the serial-ID under which the
- * exchange should save the policy_details in the deposit table.
- * @param[out] deadline On success, set to the deadline until the policy must
- * be fulfilled. Might be "forever". This value is used by an
- * external mechanism to detect timeouts.
- * @param[out] state_on_timeout On GNUNET_OK, which state shall the
- * fulfilment of this policy be put in. MUST be either
- * TALER_PolicyFulfilmentTimeoutTransfer or
- * TALER_PolicyFulfilmentTimeoutRefreshable
+ * @param[out] details On success, will contain the details to the policy,
+ * evaluated by the corresponding policy handler.
* @param[out] error_hint On error, will contain a hint
* @return GNUNET_OK if the data was accepted by the extension.
*/
- enum GNUNET_GenericReturnValue (*parse_policy_details)(
- const json_t *policy_details,
- struct GNUNET_HashCode *serial,
- struct GNUNET_TIME_Timestamp *deadline,
- enum TALER_PolicyFulfilmentState *state_on_timeout,
+ enum GNUNET_GenericReturnValue (*create_policy_details)(
+ const json_t *policy_json,
+ struct TALER_PolicyDetails *details,
const char **error_hint);
/**
@@ -197,18 +189,16 @@ struct TALER_Extension
*
* @param[in] root The JSON body from the request
* @param[in] args Additional query parameters of the request.
- * @param[in] serial_id_check Helper function to check the presence of serial_ids in policy_details. Can be used by the handler to ensure the presence of entries before starting calculations.
- * @param[out] outcome Result of the operation. Must be freed via TALER_policy_fulfilment_outcome_free after use.
+ * @param[in,out] details List of policy details related to the incoming fulfillment proof
+ * @param[in] details_len Size of the list @e details
* @param[out] output JSON output to return to the client
* @return GNUNET_OK on success.
*/
enum GNUNET_GenericReturnValue (*policy_post_handler)(
const json_t *root,
const char *const args[],
- enum GNUNET_GenericReturnValue (*serial_id_check)(struct GNUNET_HashCode
- serial_ids[], size_t
- size),
- struct TALER_PolicyFulfilmentOutcome **outcome,
+ struct TALER_PolicyDetails *details,
+ size_t details_len,
json_t **output);
/**
@@ -395,163 +385,4 @@ TALER_extensions_is_age_restriction_enabled ();
struct TALER_AgeMask
TALER_extensions_get_age_restriction_mask ();
-
-/*
- * ===================================
- * Policy extensions related API
- * ===================================
- */
-
-
-/*
- * @brief Describes the states of fulfilment of a policy bound to a deposit
- */
-enum TALER_PolicyFulfilmentState
-{
- /* Initial state of the policy */
- TALER_PolicyFulfilmentPending = 0,
-
- /*
- * Policy provably fulfilled. The semantics of the policy require that
- * the exchange MUST transfer amount in the associated deposit to the
- * payto-URI */
- TALER_PolicyFulfilmentSuccessTransfer = 1,
-
- /*
- * Policy provably fulfilled. The semantics of the policy require that
- * the coins' value in the associated deposit remains and the owner can
- * refresh them. */
- TALER_PolicyFulfilmentSuccessRefreshable = 2,
-
- /*
- * Policy provably UNfulfilled. The semantics of the policy require
- * that the exchange MUST transfer amount in the associated deposit to
- * the payto-URI. */
- TALER_PolicyFulfilmentFailureTransfer = 3,
-
- /*
- * Policy provably UNfulfilled. The semantics of the policy require that
- * the coins' value in the associated deposit remains and the owner can
- * refresh them. */
- TALER_PolicyFulfilmentFailureRefreshable = 4,
-
- /*
- * Policy timed out. The semantics of the policy require that the
- * exchange MUST transfer amount in the associated deposit to the
- * payto-URI. */
- TALER_PolicyFulfilmentTimeoutTransfer = 5,
-
- /*
- * Policy timed out. The semantics of the policy require that the
- * coins' value in the associated deposit remains and the owner can
- * refresh them. */
- TALER_PolicyFulfilmentTimeoutRefreshable = 6,
-
- TALER_PolicyFulfilmentStateMax = TALER_PolicyFulfilmentTimeoutRefreshable
-};
-
-
-const char *
-TALER_policy_fulfilment_state_str (enum TALER_PolicyFulfilmentState state);
-
-/*
- * @brief Respresents the outcome of a policy fulfilment evaluation
- * returned by a http_post_handler.
- */
-struct TALER_PolicyFulfilmentOutcome
-{
- size_t len;
- struct TALER_PolicyFulfilmentOutcomePosition
- {
- /* Identifies the particular policy in the policy_details */
- struct GNUNET_HashCode serial_id;
-
- /* The state that the policy should be be put into. */
- enum TALER_PolicyFulfilmentState state;
-
- /* If @e has_new_amount is true, the actual amount to be transfered
- * according to the @e state is not taken from the associated deposit
- * entry, but provided with @new_amount
- */
- bool has_new_amount;
- struct TALER_Amount new_amount;
-
- } *positions;
-};
-
-
-/*
- * @brief allocate a TALER_PolicyFulfilmentOutcome
- */
-struct TALER_PolicyFulfilmentOutcome *
-TALER_policy_fulfilment_outcome_new (size_t len);
-
-/*
- * @brief free the content of a TALER_PolicyFulfilmentOutcome
- */
-void
-TALER_policy_fulfilment_outcome_free (
- struct TALER_PolicyFulfilmentOutcome *outcome);
-
-
-/*
- * @brief Extracts meta information from policy_details
- *
- * @param[in] policy_details JSON of the policy detail from a deposit request
- * @param[out] serial On GNUNET_OK, the hash code that should be used to save the policy_details in the policy_details table
- * @param[out] deadline On GNUNET_OK, the deadline that should be saved in the policy_details table
- * @param[out] state_on_timeout On GNUNET_OK, which state shall the fulfilment of this policy be put in
- * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed
- * @return GNUNET_OK on success, with *extension set to the correct extension.
- * GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was
- * invalid, with *error_hint maybe non-NULL.
- */
-enum GNUNET_GenericReturnValue
-TALER_extensions_extract_meta_data_from_policy_details (
- const json_t *policy_details,
- struct GNUNET_HashCode *serial,
- struct GNUNET_TIME_Timestamp *deadline,
- enum TALER_PolicyFulfilmentState *state_on_timeout,
- const char **error_hint);
-
-
-/*
- * ================================
- * Merchant refund policy
- * ================================
- */
-struct TALER_ExtensionPolicyMerchantRefundPolicyConfig
-{
- struct GNUNET_TIME_Relative max_timeout;
-};
-
-/*
- * ================================
- * Brandt-Vickrey Auctions policy
- * ================================
- */
-/*
- * @brief Configuration for Brandt-Vickrey auctions policy
- */
-struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig
-{
- uint16_t max_bidders;
- uint16_t max_prices;
- struct TALER_Amount auction_fee;
-};
-
-
-/*
- * ================================
- * Escrowed Payments policy
- * ================================
- */
-/*
- * @brief Configuration for escrowed payments policy
- */
-struct TALER_ExtensionPolicyEscrowedPaymentsConfig
-{
- struct GNUNET_TIME_Relative max_timeout;
-};
-
#endif
diff --git a/src/include/taler_extensions_policy.h b/src/include/taler_extensions_policy.h
new file mode 100644
index 00000000..cd75f2d4
--- /dev/null
+++ b/src/include/taler_extensions_policy.h
@@ -0,0 +1,199 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file include/taler_extensions_policy.h
+ * @brief Interface for policy extensions
+ * @author Özgür Kesim
+ */
+#ifndef TALER_EXTENSIONS_POLICY_H
+#define TALER_EXTENSIONS_POLICY_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_crypto_lib.h"
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+
+/*
+ * @brief Describes the states of fulfillment of a policy bound to a deposit
+ */
+enum TALER_PolicyFulfillmentState
+{
+ /* General error state of an fulfillment. */
+ TALER_PolicyFulfillmentFailure = 0,
+
+ /* The policy is not yet ready due to insufficient funding. More deposits are
+ * necessary for it to become ready . */
+ TALER_PolicyFulfillmentInsufficient = 1,
+
+ /* The policy is funded and ready, pending */
+ TALER_PolicyFulfillmentReady = 2,
+
+ /* Policy is provably fulfilled. */
+ TALER_PolicyFulfillmentSuccess = 3,
+
+ /* Policy fulfillment has timed out */
+ TALER_PolicyFulfillmentTimeout = 4,
+
+ TALER_PolicyFulfillmentStateCount = TALER_PolicyFulfillmentTimeout + 1
+};
+
+
+/*
+ * @brief Returns a string representation of the state of a policy fulfillment
+ */
+const char *
+TALER_policy_fulfillment_state_str (enum TALER_PolicyFulfillmentState state);
+
+
+/* @brief Details of a policy for a deposit request */
+struct TALER_PolicyDetails
+{
+ /* Hash code that should be used for the .policy_hash_code field when
+ * this policy is saved in the policy_details table. */
+ struct GNUNET_HashCode hash_code;
+
+ /* Content of the policy in its original JSON form */
+ const json_t *policy_json;
+
+ /* When the deadline is meat and the policy is still in "Ready" state,
+ * a timeout-handler will transfer the amount
+ * (total_amount - policy_fee - refreshable_amount)
+ * to the payto-URI from the corresponding deposit. The value
+ * amount_refreshable will be refreshable by the owner of the
+ * associated deposits's coins */
+ struct GNUNET_TIME_Timestamp deadline;
+
+ /* The amount to which this policy commits to. It must be at least as
+ * large as @e policy_fee. */
+ struct TALER_Amount commitment;
+
+ /* The total sum of contributions from coins so far to fund this
+ * policy. It must be at least as large as @commitment in order to be
+ * sufficiently funded. */
+ struct TALER_Amount accumulated_total;
+
+ /* The fee from the exchange for handling the policy. It is due when
+ * the state changes to Timeout or Success. */
+ struct TALER_Amount policy_fee;
+
+ /* The amount that will be transfered to the payto-URIs from the
+ * corresponding deposits when the fulfillment state changes to Timeout
+ * or Success. Note that a fulfillment handler can alter this upon
+ * arrival of a proof of fulfillment. The remaining amount
+ * (accumulated_amount - policy_amount - transferable_amount) */
+ struct TALER_Amount transferable_amount;
+
+ /* The state of fulfillment of a policy.
+ * - If the state is Insufficient, the client is required to call
+ * /deposit -maybe multiple times- with enough coins and the same
+ * policy details in order to reach the required amount. The state is
+ * then changed to Ready.
+ * - If the state changes to Timeout or Success, a handler will transfer
+ * the amount (total_amount - policy_fee - refreshable_amount) to the
+ * payto-URI from the corresponding deposit. The value
+ * amount_refreshable will be refreshable by the owner of the
+ * associated deposits's coins. */
+ enum TALER_PolicyFulfillmentState fulfillment_state;
+
+ /* If there is a proof of fulfillment, the row ID from the
+ * policy_fulfillment table */
+ uint64_t policy_fulfillment_id;
+ bool no_policy_fulfillment_id;
+};
+
+/*
+ * @brief All information required for the database transaction when handling a
+ * proof of fulfillment request.
+ */
+struct TALER_PolicyFulfillmentTransactionData
+{
+ /* The incoming proof, provided by a client */
+ const json_t *proof;
+
+ /* The Hash of the proof */
+ struct GNUNET_HashCode h_proof;
+
+ /* The timestamp of retrieval of the proof */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /* The ID of the proof in the policy_fulfillment table. Will be set
+ * during the transaction. Needed to fill the table
+ * policy_details_fulfillments. */
+ uint64_t fulfillment_id;
+
+ /* The list of policy details. Will be updated by the policy handler */
+ struct TALER_PolicyDetails *details;
+ size_t details_count;
+};
+
+
+
+/*
+ * @brief Extracts policy details from the deposit's policy options and the policy extensions
+ *
+ * @param[in] policy_options JSON of the policy options from a deposit request
+ * @param[out] details On GNUNET_OK, the parsed details
+ * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed
+ * @return GNUNET_OK on success, GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was
+ * invalid, with *error_hint maybe non-NULL.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_create_policy_details (
+ const json_t *policy_options,
+ struct TALER_PolicyDetails *details,
+ const char **error_hint);
+
+
+/*
+ * ================================
+ * Merchant refund policy
+ * ================================
+ */
+struct TALER_ExtensionPolicyMerchantRefundPolicyConfig
+{
+ struct GNUNET_TIME_Relative max_timeout;
+};
+
+/*
+ * ================================
+ * Brandt-Vickrey Auctions policy
+ * ================================
+ */
+/*
+ * @brief Configuration for Brandt-Vickrey auctions policy
+ */
+struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig
+{
+ uint16_t max_bidders;
+ uint16_t max_prices;
+ struct TALER_Amount auction_fee;
+};
+
+
+/*
+ * ================================
+ * Escrowed Payments policy
+ * ================================
+ */
+/*
+ * @brief Configuration for escrowed payments policy
+ */
+struct TALER_ExtensionPolicyEscrowedPaymentsConfig
+{
+ struct GNUNET_TIME_Relative max_timeout;
+};
+
+#endif