aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/auditor/taler-auditor-httpd_deposit-confirmation.c6
-rw-r--r--src/auditor/taler-auditor-sync.c3
-rw-r--r--src/auditor/taler-helper-auditor-coins.c2
-rwxr-xr-xsrc/auditor/test-auditor.sh8
-rwxr-xr-xsrc/auditor/test-revocation.sh8
-rwxr-xr-xsrc/auditor/test-sync.sh6
-rw-r--r--src/auditordb/auditor-0001.sql2
-rw-r--r--src/auditordb/pg_get_deposit_confirmations.c6
-rw-r--r--src/auditordb/pg_insert_deposit_confirmation.c4
-rw-r--r--src/exchange-tools/taler-exchange-offline.c123
-rw-r--r--src/exchange/taler-exchange-httpd.c14
-rw-r--r--src/exchange/taler-exchange-httpd.h10
-rw-r--r--src/exchange/taler-exchange-httpd_batch-deposit.c121
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c102
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c266
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.h15
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c85
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c48
-rw-r--r--src/exchange/taler-exchange-httpd_metrics.h3
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c5
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c2
-rw-r--r--src/exchangedb/common-0001.sql6
-rw-r--r--src/exchangedb/exchange-0001-part.sql100
-rw-r--r--src/exchangedb/pg_insert_records_by_table.c114
-rw-r--r--src/exchangedb/pg_lookup_records_by_table.c131
-rw-r--r--src/exchangedb/pg_lookup_serial_by_table.c18
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c278
-rw-r--r--src/exchangedb/procedures.sql139
-rw-r--r--src/exchangedb/test_exchangedb.c58
-rw-r--r--src/extensions/Makefile.am5
-rw-r--r--src/extensions/age_restriction/Makefile.am33
-rw-r--r--src/extensions/age_restriction/age_restriction.c258
-rw-r--r--src/extensions/age_restriction_helper.c73
-rw-r--r--src/extensions/extension_age_restriction.c409
-rw-r--r--src/extensions/extensions.c231
-rw-r--r--src/include/taler_auditor_service.h4
-rw-r--r--src/include/taler_auditordb_plugin.h4
-rw-r--r--src/include/taler_crypto_lib.h43
-rw-r--r--src/include/taler_exchange_service.h4
-rw-r--r--src/include/taler_exchangedb_plugin.h135
-rw-r--r--src/include/taler_extensions.h345
-rw-r--r--src/include/taler_extensions_policy.h198
-rw-r--r--src/include/taler_json_lib.h20
-rw-r--r--src/include/taler_util.h58
-rw-r--r--src/json/json.c16
-rw-r--r--src/lib/auditor_api_deposit_confirmation.c14
-rw-r--r--src/lib/exchange_api_batch_deposit.c18
-rw-r--r--src/lib/exchange_api_common.c11
-rw-r--r--src/lib/exchange_api_common.h4
-rw-r--r--src/lib/exchange_api_deposit.c16
-rw-r--r--src/lib/exchange_api_handle.c26
-rw-r--r--src/lib/exchange_api_refund.c13
-rw-r--r--src/testing/test_exchange_api.c3
-rw-r--r--src/testing/test_exchange_p2p.c3
-rw-r--r--src/testing/testing_api_cmd_auditor_deposit_confirmation.c4
-rw-r--r--src/testing/testing_api_cmd_batch_deposit.c2
-rw-r--r--src/testing/testing_api_cmd_batch_withdraw.c2
-rw-r--r--src/testing/testing_api_cmd_deposit.c2
-rw-r--r--src/testing/testing_api_cmd_withdraw.c2
-rw-r--r--src/testing/testing_api_helpers_exchange.c9
-rw-r--r--src/util/Makefile.am1
-rw-r--r--src/util/age_restriction.c111
-rw-r--r--src/util/exchange_signatures.c18
-rw-r--r--src/util/offline_signatures.c26
-rw-r--r--src/util/wallet_signatures.c18
67 files changed, 2652 insertions, 1172 deletions
diff --git a/configure.ac b/configure.ac
index a35da3345..2df8674eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -532,6 +532,7 @@ AC_CONFIG_FILES([Makefile
src/exchangedb/Makefile
src/exchange-tools/Makefile
src/extensions/Makefile
+ src/extensions/age_restriction/Makefile
src/lib/Makefile
src/kyclogic/Makefile
src/testing/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 9fa748d7f..e10ecf8d8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,5 +32,6 @@ SUBDIRS = \
auditor \
lib \
exchange-tools \
+ extensions/age_restriction \
testing \
benchmark
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.c b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
index f4d89b7ca..c7bb4f509 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.c
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
@@ -227,7 +227,7 @@ verify_and_execute_deposit_confirmation (
TALER_exchange_online_deposit_confirmation_verify (
&dc->h_contract_terms,
&dc->h_wire,
- NULL /* h_extensions! */,
+ &dc->h_policy,
dc->exchange_timestamp,
dc->wire_deadline,
dc->refund_deadline,
@@ -276,8 +276,8 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&dc.h_contract_terms),
- GNUNET_JSON_spec_fixed_auto ("h_extensions",
- &dc.h_extensions),
+ GNUNET_JSON_spec_fixed_auto ("h_policy",
+ &dc.h_policy),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&dc.h_wire),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c
index 3c4c7e4c7..a7ccf800a 100644
--- a/src/auditor/taler-auditor-sync.c
+++ b/src/auditor/taler-auditor-sync.c
@@ -117,7 +117,8 @@ static struct Table tables[] = {
{ .rt = TALER_EXCHANGEDB_RT_RECOUP},
{ .rt = TALER_EXCHANGEDB_RT_RECOUP_REFRESH },
{ .rt = TALER_EXCHANGEDB_RT_EXTENSIONS},
- { .rt = TALER_EXCHANGEDB_RT_EXTENSION_DETAILS },
+ { .rt = TALER_EXCHANGEDB_RT_POLICY_DETAILS },
+ { .rt = TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS },
{ .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS},
{ .rt = TALER_EXCHANGEDB_RT_PURSE_DECISION},
{ .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 da0ec99ec..7637e463d 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -1566,7 +1566,7 @@ deposit_cb (void *cls,
&h_wire,
&deposit->h_contract_terms,
&deposit->coin.h_age_commitment,
- NULL /* FIXME-Oec: #7270: h_extensions! */,
+ &deposit->h_policy,
&h_denom_pub,
deposit->timestamp,
&deposit->merchant_pub,
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index 34a3980d9..0390c206b 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -87,7 +87,7 @@ function stop_libeufin()
# Cleanup exchange and libeufin between runs.
function cleanup()
{
- if test ! -z ${EPID:-}
+ if test ! -z "${EPID:-}"
then
echo -n "Stopping exchange $EPID..."
kill -TERM $EPID
@@ -102,7 +102,7 @@ function cleanup()
function exit_cleanup()
{
echo "Running exit-cleanup"
- if test ! -z ${POSTGRES_PATH:-}
+ if test ! -z "${POSTGRES_PATH:-}"
then
echo "Stopping Postgres at ${POSTGRES_PATH}"
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
@@ -2047,10 +2047,10 @@ taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || exit_skip "taler-wallet
echo -n "Testing for Postgres"
# Available directly in path?
INITDB_BIN=$(command -v initdb) || true
-if [[ ! -z $INITDB_BIN ]]; then
+if [[ ! -z "$INITDB_BIN" ]]; then
echo " FOUND (in path) at" $INITDB_BIN
else
- HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || exit_skip " MISSING"
+ HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep postgres` || exit_skip " MISSING"
echo " FOUND at" `dirname $HAVE_INITDB`
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
fi
diff --git a/src/auditor/test-revocation.sh b/src/auditor/test-revocation.sh
index 22d1c86ab..2751b5c20 100755
--- a/src/auditor/test-revocation.sh
+++ b/src/auditor/test-revocation.sh
@@ -81,7 +81,7 @@ function stop_libeufin()
# Cleanup to run whenever we exit
function cleanup()
{
- if test ! -z ${EPID:-}
+ if test ! -z "${EPID:-}"
then
echo -n "Stopping exchange $EPID..."
kill -TERM $EPID
@@ -96,7 +96,7 @@ function cleanup()
function exit_cleanup()
{
echo "Running exit-cleanup"
- if test ! -z ${POSTGRES_PATH:-}
+ if test -z "${POSTGRES_PATH:-}"
then
echo "Stopping Postgres at ${POSTGRES_PATH}"
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
@@ -640,10 +640,10 @@ taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || exit_skip "taler-wallet
echo -n "Testing for Postgres"
# Available directly in path?
INITDB_BIN=$(command -v initdb) || true
-if [[ ! -z $INITDB_BIN ]]; then
+if [[ ! -z "$INITDB_BIN" ]]; then
echo " FOUND (in path) at" $INITDB_BIN
else
- HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || exit_skip " MISSING"
+ HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep postgres` || exit_skip " MISSING"
echo " FOUND at" `dirname $HAVE_INITDB`
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
fi
diff --git a/src/auditor/test-sync.sh b/src/auditor/test-sync.sh
index 174e03c6e..92fe949f7 100755
--- a/src/auditor/test-sync.sh
+++ b/src/auditor/test-sync.sh
@@ -32,7 +32,7 @@ function exit_fail() {
# Cleanup to run whenever we exit
function cleanup() {
- if test ! -z ${POSTGRES_PATH:-}
+ if test ! -z "${POSTGRES_PATH:-}"
then
${POSTGRES_PATH}/pg_ctl -D $TMPDIR stop &> /dev/null || true
fi
@@ -111,10 +111,10 @@ taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || exit_skip "taler-wallet
echo -n "Testing for Postgres"
# Available directly in path?
INITDB_BIN=$(command -v initdb) || true
-if [[ ! -z $INITDB_BIN ]]; then
+if [[ ! -z "$INITDB_BIN" ]]; then
echo " FOUND (in path) at" $INITDB_BIN
else
- HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || exit_skip " MISSING"
+ HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep postgres` || exit_skip " MISSING"
echo " FOUND at" `dirname $HAVE_INITDB`
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
fi
diff --git a/src/auditordb/auditor-0001.sql b/src/auditordb/auditor-0001.sql
index a19655d42..1fdbe78c4 100644
--- a/src/auditordb/auditor-0001.sql
+++ b/src/auditordb/auditor-0001.sql
@@ -315,7 +315,7 @@ CREATE TABLE IF NOT EXISTS deposit_confirmations
(master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE
,serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
- ,h_extensions BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
+ ,h_policy BYTEA NOT NULL CHECK (LENGTH(h_policy)=64)
,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)
,exchange_timestamp INT8 NOT NULL
,refund_deadline INT8 NOT NULL
diff --git a/src/auditordb/pg_get_deposit_confirmations.c b/src/auditordb/pg_get_deposit_confirmations.c
index 6c7fe73c4..3f0bd1e2f 100644
--- a/src/auditordb/pg_get_deposit_confirmations.c
+++ b/src/auditordb/pg_get_deposit_confirmations.c
@@ -87,8 +87,8 @@ deposit_confirmation_cb (void *cls,
&serial_id),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&dc.h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("h_extensions",
- &dc.h_extensions),
+ GNUNET_PQ_result_spec_auto_from_type ("h_policy",
+ &dc.h_policy),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&dc.h_wire),
GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
@@ -158,7 +158,7 @@ TAH_PG_get_deposit_confirmations (
"SELECT"
" serial_id"
",h_contract_terms"
- ",h_extensions"
+ ",h_policy"
",h_wire"
",exchange_timestamp"
",wire_deadline"
diff --git a/src/auditordb/pg_insert_deposit_confirmation.c b/src/auditordb/pg_insert_deposit_confirmation.c
index cc52ba4e5..675f8ed0d 100644
--- a/src/auditordb/pg_insert_deposit_confirmation.c
+++ b/src/auditordb/pg_insert_deposit_confirmation.c
@@ -35,7 +35,7 @@ TAH_PG_insert_deposit_confirmation (
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&dc->master_public_key),
GNUNET_PQ_query_param_auto_from_type (&dc->h_contract_terms),
- GNUNET_PQ_query_param_auto_from_type (&dc->h_extensions),
+ GNUNET_PQ_query_param_auto_from_type (&dc->h_policy),
GNUNET_PQ_query_param_auto_from_type (&dc->h_wire),
GNUNET_PQ_query_param_timestamp (&dc->exchange_timestamp),
GNUNET_PQ_query_param_timestamp (&dc->wire_deadline),
@@ -54,7 +54,7 @@ TAH_PG_insert_deposit_confirmation (
"INSERT INTO deposit_confirmations "
"(master_pub"
",h_contract_terms"
- ",h_extensions"
+ ",h_policy"
",h_wire"
",exchange_timestamp"
",wire_deadline"
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
index 26ddf738d..a8a25bd3c 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -138,6 +138,12 @@ static struct GNUNET_CURL_RescheduleContext *rc;
static const struct GNUNET_CONFIGURATION_Handle *kcfg;
/**
+ * Age restriction configuration
+ */
+static bool ar_enabled = false;
+static struct TALER_AgeRestrictionConfig ar_config = {0};
+
+/**
* Return value from main().
*/
static int global_ret;
@@ -164,11 +170,6 @@ static char *currency;
static char *CFG_exchange_url;
/**
- * If age restriction is enabled, the age mask to be used
- */
-static struct TALER_AgeMask age_mask = {0};
-
-/**
* A subcommand supported by this program.
*/
struct SubCommand
@@ -2159,14 +2160,14 @@ upload_extensions (const char *exchange_url,
/* 2. Verify the signature */
{
- struct TALER_ExtensionConfigHashP h_config;
+ struct TALER_ExtensionManifestsHashP h_manifests;
if (GNUNET_OK !=
- TALER_JSON_extensions_config_hash (extensions, &h_config))
+ TALER_JSON_extensions_manifests_hash (extensions, &h_manifests))
{
GNUNET_JSON_parse_free (spec);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "couldn't hash extensions\n");
+ "couldn't hash extensions' manifests\n");
global_ret = EXIT_FAILURE;
test_shutdown ();
return;
@@ -2176,8 +2177,8 @@ upload_extensions (const char *exchange_url,
load_offline_key (GNUNET_NO))
return;
- if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
- &h_config,
+ if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify (
+ &h_manifests,
&master_pub,
&sig))
{
@@ -3858,7 +3859,7 @@ load_age_mask (const char*section_name)
static const struct TALER_AgeMask null_mask = {0};
enum GNUNET_GenericReturnValue ret;
- if (age_mask.bits == 0)
+ if (! ar_enabled)
return null_mask;
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
@@ -3870,14 +3871,14 @@ load_age_mask (const char*section_name)
ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
section_name,
"AGE_RESTRICTED");
- if (GNUNET_YES == ret)
- return age_mask;
-
if (GNUNET_SYSERR == ret)
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section_name,
"AGE_RESTRICTED",
"Value must be YES or NO\n");
+ if (GNUNET_YES == ret)
+ return ar_config.mask;
+
return null_mask;
}
@@ -4218,18 +4219,24 @@ do_setup (char *const *args)
static void
do_extensions_show (char *const *args)
{
- const struct TALER_Extension *it;
+ const struct TALER_Extensions *it;
json_t *exts = json_object ();
json_t *obj;
GNUNET_assert (NULL != exts);
for (it = TALER_extensions_get_head ();
- NULL != it;
+ NULL != it && NULL != it->extension;
it = it->next)
- GNUNET_assert (0 ==
- json_object_set_new (exts,
- it->name,
- it->config_to_json (it)));
+ {
+ const struct TALER_Extension *extension = it->extension;
+ int ret;
+
+ ret = json_object_set_new (exts,
+ extension->name,
+ extension->manifest (extension));
+ GNUNET_assert (-1 != ret);
+ }
+
obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_steal ("extensions",
exts));
@@ -4238,7 +4245,7 @@ do_extensions_show (char *const *args)
json_dumps (obj,
JSON_INDENT (2)));
json_decref (obj);
- next (args + 1);
+ next (args);
}
@@ -4249,35 +4256,38 @@ static void
do_extensions_sign (char *const *args)
{
json_t *extensions = json_object ();
- struct TALER_ExtensionConfigHashP h_config;
+ struct TALER_ExtensionManifestsHashP h_manifests;
struct TALER_MasterSignatureP sig;
- const struct TALER_Extension *it;
+ const struct TALER_Extensions *it;
+ bool found = false;
json_t *obj;
GNUNET_assert (NULL != extensions);
- if (GNUNET_OK !=
- TALER_extensions_load_taler_config (kcfg))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "error while loading taler config for extensions\n");
- json_decref (extensions);
- return;
- }
for (it = TALER_extensions_get_head ();
- NULL != it;
+ NULL != it && NULL != it->extension;
it = it->next)
+ {
+ const struct TALER_Extension *ext = it->extension;
+ GNUNET_assert (ext);
+
+ found = true;
+
GNUNET_assert (0 ==
json_object_set_new (extensions,
- it->name,
- it->config_to_json (it)));
+ ext->name,
+ ext->manifest (ext)));
+ }
+
+ if (! found)
+ return;
if (GNUNET_OK !=
- TALER_JSON_extensions_config_hash (extensions,
- &h_config))
+ TALER_JSON_extensions_manifests_hash (extensions,
+ &h_manifests))
{
json_decref (extensions);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "error while hashing config for extensions\n");
+ "error while hashing manifest for extensions\n");
return;
}
@@ -4288,18 +4298,19 @@ do_extensions_sign (char *const *args)
return;
}
- TALER_exchange_offline_extension_config_hash_sign (&h_config,
- &master_priv,
- &sig);
+ TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests,
+ &master_priv,
+ &sig);
obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_steal ("extensions",
extensions),
GNUNET_JSON_pack_data_auto (
"extensions_sig",
&sig));
+
output_operation (OP_EXTENSIONS,
obj);
- next (args + 1);
+ next (args);
}
@@ -4500,25 +4511,31 @@ run (void *cls,
(void) cls;
(void) cfgfile;
kcfg = cfg;
- if (GNUNET_OK !=
- TALER_config_get_currency (kcfg,
- &currency))
+
+ /* load extensions */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_extensions_init (kcfg));
+
+ /* setup age restriction, if applicable */
{
- global_ret = EXIT_NOTCONFIGURED;
- return;
+ const struct TALER_AgeRestrictionConfig *arc;
+
+ if (NULL !=
+ (arc = TALER_extensions_get_age_restriction_config ()))
+ {
+ ar_config = *arc;
+ ar_enabled = true;
+ }
}
- /* load age mask, if age restriction is enabled */
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
- if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
+ if (GNUNET_OK !=
+ TALER_config_get_currency (kcfg,
+ &currency))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "error while loading taler config for extensions\n");
+ global_ret = EXIT_NOTCONFIGURED;
return;
}
- age_mask = TALER_extensions_age_restriction_ageMask ();
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 34f93879a..691e1ef7d 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -102,6 +102,14 @@ static int allow_address_reuse;
const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
/**
+ * Configuration of age restriction
+ *
+ * Set after loading the library, enabled in database event handler.
+ */
+bool TEH_age_restriction_enabled = false;
+struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0};
+
+/**
* Handle to the HTTP server.
*/
static struct MHD_Daemon *mhd;
@@ -143,11 +151,6 @@ char *TEH_currency;
char *TEH_base_url;
/**
- * Age restriction flags and mask
- */
-bool TEH_age_restriction_enabled = true;
-
-/**
* Default timeout in seconds for HTTP requests.
*/
static unsigned int connection_timeout = 30;
@@ -174,6 +177,7 @@ bool TEH_suicide;
* TALER_SIGNATURE_MASTER_EXTENSION.
*/
struct TALER_MasterSignatureP TEH_extensions_sig;
+bool TEH_extensions_signed = false;
/**
* Value to return from main()
diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h
index 0fda5ed8d..4d3fb4901 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/
extern char *TEH_currency;
-/*
- * Age restriction extension state
- */
-extern bool TEH_age_restriction_enabled;
-
/**
* Our (externally visible) base URL.
*/
@@ -221,6 +216,7 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
* Signature of the offline master key of all enabled extensions' configuration
*/
extern struct TALER_MasterSignatureP TEH_extensions_sig;
+extern bool TEH_extensions_signed;
/**
* @brief Struct describing an URL and the handler for it.
@@ -366,4 +362,8 @@ struct TEH_RequestHandler
};
+/* Age restriction configuration */
+extern bool TEH_age_restriction_enabled;
+extern struct TALER_AgeRestrictionConfig TEH_age_restriction_config;
+
#endif
diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c b/src/exchange/taler-exchange-httpd_batch-deposit.c
index c2a9cbd54..4d4197ab6 100644
--- a/src/exchange/taler-exchange-httpd_batch-deposit.c
+++ b/src/exchange/taler-exchange-httpd_batch-deposit.c
@@ -87,15 +87,27 @@ struct BatchDepositContext
const char *payto_uri;
/**
- * Additional details for extensions relevant for this
+ * Additional details for policy extension relevant for this
* deposit operation, possibly NULL!
*/
- json_t *extension_details;
+ json_t *policy_json;
/**
- * Hash over @e extension_details.
+ * Will be true if policy_json were provided
*/
- struct TALER_ExtensionContractHashP h_extensions;
+ bool has_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;
+
+ /**
+ * Hash over @e policy_details.
+ */
+ struct TALER_ExtensionPolicyHashP h_policy;
/**
* Time when this request was generated. Used, for example, to
@@ -173,7 +185,7 @@ again:
&TEH_keys_exchange_sign_,
&bdc->h_contract_terms,
&bdc->h_wire,
- &bdc->h_extensions,
+ bdc->has_policy ? &bdc->h_policy: NULL,
bdc->exchange_timestamp,
bdc->wire_deadline,
bdc->refund_deadline,
@@ -242,7 +254,7 @@ batch_deposit_transaction (void *cls,
MHD_RESULT *mhd_ret)
{
struct BatchDepositContext *dc = cls;
- enum GNUNET_DB_QueryStatus qs;
+ enum GNUNET_DB_QueryStatus qs = GNUNET_SYSERR;
bool balance_ok;
bool in_conflict;
@@ -469,18 +481,19 @@ parse_coin (struct MHD_Connection *connection,
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
- TALER_wallet_deposit_verify (&deposit->amount_with_fee,
- &deposit->deposit_fee,
- &dc->h_wire,
- &dc->h_contract_terms,
- &deposit->coin.h_age_commitment,
- &dc->h_extensions,
- &deposit->coin.denom_pub_hash,
- dc->timestamp,
- &dc->merchant_pub,
- dc->refund_deadline,
- &deposit->coin.coin_pub,
- &deposit->csig))
+ TALER_wallet_deposit_verify (
+ &deposit->amount_with_fee,
+ &deposit->deposit_fee,
+ &dc->h_wire,
+ &dc->h_contract_terms,
+ &deposit->coin.h_age_commitment,
+ dc->has_policy ? &dc->h_policy : NULL,
+ &deposit->coin.denom_pub_hash,
+ dc->timestamp,
+ &dc->merchant_pub,
+ dc->refund_deadline,
+ &deposit->coin.coin_pub,
+ &deposit->csig))
{
TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
GNUNET_JSON_parse_free (spec);
@@ -496,11 +509,6 @@ parse_coin (struct MHD_Connection *connection,
deposit->h_contract_terms = dc->h_contract_terms;
deposit->wire_salt = dc->wire_salt;
deposit->receiver_wire_account = (char *) dc->payto_uri;
- /* FIXME-OEC: #7270 should NOT insert the extension details N times,
- 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->extension_details = dc->extension_details;
deposit->timestamp = dc->timestamp;
deposit->refund_deadline = dc->refund_deadline;
deposit->wire_deadline = dc->wire_deadline;
@@ -517,7 +525,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
struct BatchDepositContext dc;
json_t *coins;
bool no_refund_deadline = true;
- bool no_extensions = true;
+ bool no_policy_json = true;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("merchant_payto_uri",
&dc.payto_uri),
@@ -530,9 +538,9 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
GNUNET_JSON_spec_json ("coins",
&coins),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("extension_details",
- &dc.extension_details),
- &no_extensions),
+ GNUNET_JSON_spec_json ("policy",
+ &dc.policy_json),
+ &no_policy_json),
GNUNET_JSON_spec_timestamp ("timestamp",
&dc.timestamp),
GNUNET_JSON_spec_mark_optional (
@@ -563,6 +571,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
return MHD_YES; /* failure */
}
+ dc.has_policy = ! no_policy_json;
+
/* validate merchant's wire details (as far as we can) */
{
char *emsg;
@@ -607,11 +617,26 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
TALER_merchant_wire_signature_hash (dc.payto_uri,
&dc.wire_salt,
&dc.h_wire);
- /* FIXME-OEC: #7270 hash actual extension JSON object here */
- // if (! no_extensions)
- memset (&dc.h_extensions,
- 0,
- sizeof (dc.h_extensions));
+
+ /* handle policy, if present */
+ if (dc.has_policy)
+ {
+ 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)
{
@@ -635,12 +660,32 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
struct TALER_EXCHANGEDB_Deposit);
for (unsigned int i = 0; i<dc.num_coins; i++)
{
- if (GNUNET_OK !=
- (res = parse_coin (connection,
- json_array_get (coins,
- i),
- &dc,
- &dc.deposits[i])))
+ do {
+ res = parse_coin (connection,
+ json_array_get (coins, i),
+ &dc,
+ &dc.deposits[i]);
+ if (GNUNET_OK != res)
+ break;
+
+ /* If applicable, accumulate all contributions into the policy_details */
+ if (dc.has_policy)
+ {
+ /* FIXME: how do deposit-fee and policy-fee interact? */
+ struct TALER_Amount amount_without_fee;
+
+ res = TALER_amount_subtract (&amount_without_fee,
+ &dc.deposits[i].amount_with_fee,
+ &dc.deposits[i].deposit_fee
+ );
+ res = TALER_amount_add (
+ &dc.policy_details.accumulated_total,
+ &dc.policy_details.accumulated_total,
+ &amount_without_fee);
+ }
+ } while(0);
+
+ if (GNUNET_OK != res)
{
for (unsigned int j = 0; j<i; j++)
TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 0484ab071..455888a89 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -21,6 +21,7 @@
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@@ -47,7 +48,7 @@
* @param connection connection to the client
* @param coin_pub public key of the coin
* @param h_wire hash of wire details
- * @param h_extensions hash of applicable extensions
+ * @param h_policy hash of applicable policy extension
* @param h_contract_terms hash of contract details
* @param exchange_timestamp exchange's timestamp
* @param refund_deadline until when this deposit be refunded
@@ -61,7 +62,7 @@ reply_deposit_success (
struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_PrivateContractHashP *h_contract_terms,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -78,7 +79,7 @@ reply_deposit_success (
&TEH_keys_exchange_sign_,
h_contract_terms,
h_wire,
- h_extensions,
+ h_policy,
exchange_timestamp,
wire_deadline,
refund_deadline,
@@ -131,6 +132,29 @@ struct DepositContext
*/
uint64_t known_coin_id;
+ /*
+ * True if @e policy_json was provided
+ */
+ bool has_policy;
+
+ /**
+ * If @e has_policy is true, the corresponding policy extension calculates
+ * these details. These will be persisted in the policy_details table.
+ */
+ struct TALER_PolicyDetails policy_details;
+
+ /**
+ * 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;
+
+ /**
+ * When 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;
+
};
@@ -163,14 +187,35 @@ deposit_transaction (void *cls,
mhd_ret);
if (qs < 0)
return qs;
- qs = TEH_plugin->do_deposit (TEH_plugin->cls,
- dc->deposit,
- dc->known_coin_id,
- &dc->h_payto,
- false, /* FIXME-OEC: extension blocked #7270 */
- &dc->exchange_timestamp,
- &balance_ok,
- &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;
+ }
+
+
+ qs = TEH_plugin->do_deposit (
+ TEH_plugin->cls,
+ dc->deposit,
+ dc->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)
@@ -216,6 +261,8 @@ TEH_handler_deposit (struct MHD_Connection *connection,
struct DepositContext dc;
struct TALER_EXCHANGEDB_Deposit deposit;
const char *payto_uri;
+ struct TALER_ExtensionPolicyHashP *ph_policy = NULL;
+ bool no_policy_json;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("merchant_payto_uri",
&payto_uri),
@@ -240,12 +287,18 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.csig),
GNUNET_JSON_spec_timestamp ("timestamp",
&deposit.timestamp),
+ /* TODO: refund_deadline and merchant_pub will move into the
+ * extension policy_merchant_refunds */
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("refund_deadline",
&deposit.refund_deadline),
NULL),
GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
&deposit.wire_deadline),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("policy",
+ &dc.policy_details.policy_json),
+ &no_policy_json),
GNUNET_JSON_spec_end ()
};
struct TALER_MerchantWireHashP h_wire;
@@ -271,6 +324,9 @@ TEH_handler_deposit (struct MHD_Connection *connection,
return MHD_YES; /* failure */
}
}
+
+ dc.has_policy = ! no_policy_json;
+
/* validate merchant's wire details (as far as we can) */
{
char *emsg;
@@ -419,6 +475,26 @@ TEH_handler_deposit (struct MHD_Connection *connection,
NULL);
}
+ /* Check policy input and create policy details */
+ if (dc.has_policy)
+ {
+ const char *error_hint = NULL;
+
+ if (GNUNET_OK !=
+ TALER_extensions_create_policy_details (
+ dc.policy_details.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_details.policy_json,
+ &dc.h_policy);
+ ph_policy = &dc.h_policy;
+ }
+
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
TALER_wallet_deposit_verify (&deposit.amount_with_fee,
@@ -426,7 +502,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&h_wire,
&deposit.h_contract_terms,
&deposit.coin.h_age_commitment,
- NULL /* FIXME: h_extensions! */,
+ ph_policy,
&deposit.coin.denom_pub_hash,
deposit.timestamp,
&deposit.merchant_pub,
@@ -481,7 +557,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
res = reply_deposit_success (connection,
&deposit.coin.coin_pub,
&h_wire,
- NULL /* FIXME: h_extensions! */,
+ ph_policy,
&deposit.h_contract_terms,
dc.exchange_timestamp,
deposit.refund_deadline,
diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c
index d6c26f6f4..30d1c5ac9 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"
@@ -77,65 +77,84 @@ extension_update_event_cb (void *cls,
return;
}
- // Get the config from the database as string
+ // Get the manifest from the database as string
{
- char *config_str = NULL;
+ char *manifest_str = NULL;
enum GNUNET_DB_QueryStatus qs;
json_error_t err;
- json_t *config;
+ json_t *manifest_js;
enum GNUNET_GenericReturnValue ret;
- qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
- extension->name,
- &config_str);
+ qs = TEH_plugin->get_extension_manifest (TEH_plugin->cls,
+ extension->name,
+ &manifest_str);
if (qs < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Couldn't get extension config\n");
+ "Couldn't get extension manifest\n");
GNUNET_break (0);
return;
}
// No config found -> disable extension
- if (NULL == config_str)
+ if (NULL == manifest_str)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No manifest found for extension %s, disabling it\n",
+ extension->name);
extension->disable ((struct TALER_Extension *) extension);
return;
}
// Parse the string as JSON
- config = json_loads (config_str, JSON_DECODE_ANY, &err);
- if (NULL == config)
+ manifest_js = json_loads (manifest_str, JSON_DECODE_ANY, &err);
+ if (NULL == manifest_js)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse config for extension `%s' as JSON: %s (%s)\n",
+ "Failed to parse manifest for extension `%s' as JSON: %s (%s)\n",
extension->name,
err.text,
err.source);
GNUNET_break (0);
+ free (manifest_js);
return;
}
// Call the parser for the extension
- ret = extension->load_json_config (
+ ret = extension->load_config (
(struct TALER_Extension *) extension,
- config);
+ json_object_get (manifest_js, "config"));
if (GNUNET_OK != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Couldn't parse configuration for extension %s from the database",
- extension->name);
+ "Couldn't parse configuration for extension %s from the manifest in the database: %s\n",
+ extension->name,
+ manifest_str);
GNUNET_break (0);
}
+
+ free (manifest_str);
+ json_decref (manifest_js);
}
/* Special case age restriction: Update global flag and mask */
if (TALER_Extension_AgeRestriction == type)
{
- TEH_age_restriction_enabled =
- TALER_extensions_age_restriction_is_enabled ();
+ const struct TALER_AgeRestrictionConfig *conf =
+ TALER_extensions_get_age_restriction_config ();
+ TEH_age_restriction_enabled = false;
+ if (NULL != conf)
+ {
+ TEH_age_restriction_enabled = true;
+ TEH_age_restriction_config = *conf;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "[age restriction] DB event has changed the config to %s with mask: %s\n",
+ TEH_age_restriction_enabled ? "enabled": "DISABLED",
+ TALER_age_mask_to_string (&conf->mask));
+
}
}
@@ -143,14 +162,30 @@ extension_update_event_cb (void *cls,
enum GNUNET_GenericReturnValue
TEH_extensions_init ()
{
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
-
/* Set the event handler for updates */
struct GNUNET_DB_EventHeaderP ev = {
.size = htons (sizeof (ev)),
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
};
+
+ /* Load the shared libraries first */
+ if (GNUNET_OK !=
+ TALER_extensions_init (TEH_cfg))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "failed to load extensions");
+ return GNUNET_SYSERR;
+ }
+
+ /* Check for age restriction */
+ {
+ const struct TALER_AgeRestrictionConfig *arc;
+
+ if (NULL !=
+ (arc = TALER_extensions_get_age_restriction_config ()))
+ TEH_age_restriction_config = *arc;
+ }
+
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
GNUNET_TIME_UNIT_FOREVER_REL,
&ev,
@@ -162,17 +197,24 @@ TEH_extensions_init ()
return GNUNET_SYSERR;
}
- /* FIXME #7270: shall we load the extensions from the config right away?
- * We do have to for now, as otherwise denominations with age restriction
- * will not have the age mask set right upon initial generation.
- */
- TALER_extensions_load_taler_config (TEH_cfg);
-
/* Trigger the initial load of configuration from the db */
- for (const struct TALER_Extension *it = TALER_extensions_get_head ();
- NULL != it->next;
+ for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
+ NULL != it && NULL != it->extension;
it = it->next)
- extension_update_event_cb (NULL, &it->type, sizeof(it->type));
+ {
+ const struct TALER_Extension *ext = it->extension;
+ uint32_t typ = htonl (ext->type);
+ char *manifest = json_dumps (ext->manifest (ext), JSON_COMPACT);
+
+ TEH_plugin->set_extension_manifest (TEH_plugin->cls,
+ ext->name,
+ manifest);
+
+ extension_update_event_cb (NULL,
+ &typ,
+ sizeof(typ));
+ free (manifest);
+ }
return GNUNET_OK;
}
@@ -190,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_FULFILLMENT,
+ &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 4659b653e..e435f8f03 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_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 9b7f28bd0..30c336539 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -17,6 +17,7 @@
* @file taler-exchange-httpd_keys.c
* @brief management of our various keys
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include "taler_json_lib.h"
@@ -815,10 +816,7 @@ static struct TALER_AgeMask
load_age_mask (const char*section_name)
{
static const struct TALER_AgeMask null_mask = {0};
- struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask ();
-
- if (age_mask.bits == 0)
- return null_mask;
+ enum GNUNET_GenericReturnValue ret;
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
TEH_cfg,
@@ -826,22 +824,29 @@ load_age_mask (const char*section_name)
"AGE_RESTRICTED")))
return null_mask;
+ if (GNUNET_SYSERR ==
+ (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
+ section_name,
+ "AGE_RESTRICTED")))
{
- enum GNUNET_GenericReturnValue ret;
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section_name,
+ "AGE_RESTRICTED",
+ "Value must be YES or NO\n");
+ return null_mask;
+ }
- if (GNUNET_SYSERR ==
- (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
- section_name,
- "AGE_RESTRICTED")))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- section_name,
- "AGE_RESTRICTED",
- "Value must be YES or NO\n");
- return null_mask;
- }
+ if (GNUNET_OK == ret)
+ {
+ if (! TEH_age_restriction_enabled)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "age restriction set in section %s, yet, age restriction is not enabled\n",
+ section_name);
+ return TEH_age_restriction_config.mask;
}
- return age_mask;
+
+
+ return null_mask;
}
@@ -1898,41 +1903,29 @@ create_krd (struct TEH_KeyStateHandle *ksh,
bool has_extensions = false;
/* Fill in the configurations of the enabled extensions */
- for (const struct TALER_Extension *extension = TALER_extensions_get_head ();
- NULL != extension;
- extension = extension->next)
+ for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
+ NULL != iter && NULL != iter->extension;
+ iter = iter->next)
{
- json_t *ext;
- json_t *config_json;
+ const struct TALER_Extension *extension = iter->extension;
+ json_t *manifest;
int r;
- /* skip if not configured == disabled */
- if (NULL == extension->config ||
- NULL == extension->config_json)
+ /* skip if not enabled */
+ if (! extension->enabled)
continue;
/* flag our findings so far */
has_extensions = true;
- GNUNET_assert (NULL != extension->config_json);
- config_json = json_copy (extension->config_json);
- GNUNET_assert (NULL != config_json);
-
- ext = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_bool ("critical",
- extension->critical),
- GNUNET_JSON_pack_string ("version",
- extension->version),
- GNUNET_JSON_pack_object_steal ("config",
- config_json)
- );
- GNUNET_assert (NULL != ext);
+ manifest = extension->manifest (extension);
+ GNUNET_assert (manifest);
r = json_object_set_new (
extensions,
extension->name,
- ext);
+ manifest);
GNUNET_assert (0 == r);
}
@@ -1948,12 +1941,16 @@ create_krd (struct TEH_KeyStateHandle *ksh,
extensions);
GNUNET_assert (0 == r);
- sig = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("extensions_sig",
- &TEH_extensions_sig));
+ /* Add the signature of the extensions, if it is not zero */
+ if (TEH_extensions_signed)
+ {
+ sig = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("extensions_sig",
+ &TEH_extensions_sig));
- r = json_object_update (keys, sig);
- GNUNET_assert (0 == r);
+ r = json_object_update (keys, sig);
+ GNUNET_assert (0 == r);
+ }
}
else
{
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c
index a663b1b06..989b88fb6 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -38,7 +38,7 @@
struct Extension
{
enum TALER_Extension_Type type;
- json_t *config;
+ json_t *manifest;
};
/**
@@ -52,7 +52,7 @@ struct SetExtensionsContext
};
/**
- * Function implementing database transaction to set the configuration of
+ * Function implementing database transaction to set the manifests of
* extensions. It runs the transaction logic.
* - IF it returns a non-error code, the transaction logic MUST NOT queue a
* MHD response.
@@ -74,13 +74,13 @@ set_extensions (void *cls,
{
struct SetExtensionsContext *sec = cls;
- /* save the configurations of all extensions */
+ /* save the manifests of all extensions */
for (uint32_t i = 0; i<sec->num_extensions; i++)
{
struct Extension *ext = &sec->extensions[i];
const struct TALER_Extension *taler_ext;
enum GNUNET_DB_QueryStatus qs;
- char *config;
+ char *manifest;
taler_ext = TALER_extensions_get_by_type (ext->type);
if (NULL == taler_ext)
@@ -90,10 +90,8 @@ set_extensions (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
- GNUNET_assert (NULL != ext->config);
-
- config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS);
- if (NULL == config)
+ manifest = json_dumps (ext->manifest, JSON_COMPACT | JSON_SORT_KEYS);
+ if (NULL == manifest)
{
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -103,10 +101,12 @@ set_extensions (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
- qs = TEH_plugin->set_extension_config (
+ qs = TEH_plugin->set_extension_manifest (
TEH_plugin->cls,
taler_ext->name,
- config);
+ manifest);
+
+ free (manifest);
if (qs < 0)
{
@@ -137,6 +137,7 @@ set_extensions (void *cls,
/* All extensions configured, update the signature */
TEH_extensions_sig = sec->extensions_sig;
+ TEH_extensions_signed = true;
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
}
@@ -150,7 +151,7 @@ verify_extensions_from_json (
const char*name;
const struct TALER_Extension *extension;
size_t i = 0;
- json_t *blob;
+ json_t *manifest;
GNUNET_assert (NULL != extensions);
GNUNET_assert (json_is_object (extensions));
@@ -159,7 +160,7 @@ verify_extensions_from_json (
sec->extensions = GNUNET_new_array (sec->num_extensions,
struct Extension);
- json_object_foreach (extensions, name, blob)
+ json_object_foreach (extensions, name, manifest)
{
int critical = 0;
json_t *config;
@@ -175,18 +176,18 @@ verify_extensions_from_json (
}
if (GNUNET_OK !=
- TALER_extensions_is_json_config (
- blob, &critical, &version, &config))
+ TALER_extensions_parse_manifest (
+ manifest, &critical, &version, &config))
return GNUNET_SYSERR;
if (critical != extension->critical
|| 0 != strcmp (version, extension->version) // FIXME-oec: libtool compare
|| NULL == config
- || GNUNET_OK != extension->test_json_config (config))
+ || GNUNET_OK != extension->load_config (NULL, config))
return GNUNET_SYSERR;
sec->extensions[i].type = extension->type;
- sec->extensions[i].config = config;
+ sec->extensions[i].manifest = json_copy (manifest);
}
return GNUNET_OK;
@@ -223,7 +224,8 @@ TEH_handler_management_post_extensions (
}
/* Ensure we have an object */
- if (! json_is_object (extensions))
+ if ((! json_is_object (extensions)) &&
+ (! json_is_null (extensions)))
{
GNUNET_JSON_parse_free (top_spec);
return TALER_MHD_reply_with_error (
@@ -235,13 +237,13 @@ TEH_handler_management_post_extensions (
/* Verify the signature */
{
- struct TALER_ExtensionConfigHashP h_config;
+ struct TALER_ExtensionManifestsHashP h_manifests;
if (GNUNET_OK !=
- TALER_JSON_extensions_config_hash (extensions, &h_config) ||
+ TALER_JSON_extensions_manifests_hash (extensions, &h_manifests) ||
GNUNET_OK !=
- TALER_exchange_offline_extension_config_hash_verify (
- &h_config,
+ TALER_exchange_offline_extension_manifests_hash_verify (
+ &h_manifests,
&TEH_master_public_key,
&sec.extensions_sig))
{
@@ -298,9 +300,9 @@ TEH_handler_management_post_extensions (
CLEANUP:
for (unsigned int i = 0; i < sec.num_extensions; i++)
{
- if (NULL != sec.extensions[i].config)
+ if (NULL != sec.extensions[i].manifest)
{
- json_decref (sec.extensions[i].config);
+ json_decref (sec.extensions[i].manifest);
}
}
GNUNET_free (sec.extensions);
diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h
index c1b7326ff..cae371f7c 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_FULFILLMENT = 13,
+ TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
};
/**
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index a25d6ff43..85090cedc 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations (
bool failed = true;
/* Has been checked in handle_refreshes_reveal_json() */
- GNUNET_assert (ng ==
- TALER_extensions_age_restriction_num_groups ());
+ GNUNET_assert (ng == TEH_age_restriction_config.num_groups);
rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment);
oac = rctx->old_age_commitment;
@@ -931,7 +930,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
/* Sanity check of age commitment: If it was provided, it _must_ be an array
* of the size the # of age groups */
if (NULL != old_age_commitment_json
- && TALER_extensions_age_restriction_num_groups () !=
+ && TEH_age_restriction_config.num_groups !=
json_array_size (old_age_commitment_json))
{
GNUNET_break_op (0);
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index a81d1b6e5..ca110ad45 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -76,7 +76,7 @@ TEH_RESPONSE_compile_transaction_history (
&h_wire,
&deposit->h_contract_terms,
&deposit->h_age_commitment,
- NULL /* h_extensions! */,
+ &deposit->h_policy,
&deposit->h_denom_pub,
deposit->timestamp,
&deposit->merchant_pub,
diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql
index ab4f8ea91..a95d74d2a 100644
--- a/src/exchangedb/common-0001.sql
+++ b/src/exchangedb/common-0001.sql
@@ -907,8 +907,8 @@ BEGIN
',wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)'
',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
',done BOOLEAN NOT NULL DEFAULT FALSE'
- ',extension_blocked BOOLEAN NOT NULL DEFAULT FALSE'
- ',extension_details_serial_id INT8' -- REFERENCES extension_details (extension_details_serial_id) ON DELETE CASCADE'
+ ',policy_blocked BOOLEAN NOT NULL DEFAULT FALSE'
+ ',policy_details_serial_id INT8' -- REFERENCES policy_details (policy_details_serial_id) ON DELETE CASCADE'
') %s ;'
,table_name
,'PARTITION BY HASH (coin_pub)'
@@ -2619,7 +2619,7 @@ BEGIN
ALTER TABLE IF EXISTS deposits
DROP CONSTRAINT IF EXISTS deposits_pkey CASCADE
- ,DROP CONSTRAINT IF EXISTS deposits_extension_details_serial_id_fkey
+ ,DROP CONSTRAINT IF EXISTS deposits_policy_details_serial_id_fkey
,DROP CONSTRAINT IF EXISTS deposits_coin_pub_merchant_pub_h_contract_terms_key CASCADE
;
diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql
index 99883a279..4599d2ee7 100644
--- a/src/exchangedb/exchange-0001-part.sql
+++ b/src/exchangedb/exchange-0001-part.sql
@@ -411,19 +411,19 @@ COMMENT ON TABLE signkey_revocations
IS 'Table storing which online signing keys have been revoked';
--- ------------------------------ extension ----------------------------------------
+-- ------------------------------ extensions ----------------------------------------
CREATE TABLE IF NOT EXISTS extensions
(extension_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,name VARCHAR NOT NULL UNIQUE
- ,config BYTEA
+ ,manifest BYTEA
);
COMMENT ON TABLE extensions
IS 'Configurations of the activated extensions';
COMMENT ON COLUMN extensions.name
IS 'Name of the extension';
-COMMENT ON COLUMN extensions.config
- IS 'Configuration of the extension as JSON-blob, maybe NULL';
+COMMENT ON COLUMN extensions.manifest
+ IS 'Manifest of the extension as JSON-blob, maybe NULL. It contains common meta-information and extension-specific configuration.';
-- ------------------------------ known_coins ----------------------------------------
@@ -520,21 +520,69 @@ CREATE TABLE IF NOT EXISTS refresh_transfer_keys_default
SELECT add_constraints_to_refresh_transfer_keys_partition('default');
--- ------------------------------ extension_details ----------------------------------------
-
-CREATE TABLE IF NOT EXISTS extension_details
- (extension_details_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
- ,extension_options VARCHAR)
- PARTITION BY HASH (extension_details_serial_id);
-COMMENT ON TABLE extension_details
- IS 'Extensions that were provided with deposits (not yet used).';
-COMMENT ON COLUMN extension_details.extension_options
- IS 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the extensions supported by the exchange.';
-
-CREATE TABLE IF NOT EXISTS extension_details_default
- PARTITION OF extension_details
- 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
+ ,policy_hash_code BYTEA PRIMARY KEY CHECK(LENGTH(policy_hash_code)=16)
+ ,policy_json VARCHAR
+ ,deadline INT8 NOT NULL
+ ,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.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_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 (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 ----------------------------------------
@@ -552,10 +600,10 @@ COMMENT ON COLUMN deposits.wire_salt
IS 'Salt used when hashing the payto://-URI to get the h_wire';
COMMENT ON COLUMN deposits.done
IS 'Set to TRUE once we have included this deposit in some aggregate wire transfer to the merchant';
-COMMENT ON COLUMN deposits.extension_blocked
- IS 'True if the aggregation of the deposit is currently blocked by some extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.';
-COMMENT ON COLUMN deposits.extension_details_serial_id
- IS 'References extensions table, NULL if extensions are not used';
+COMMENT ON COLUMN deposits.policy_blocked
+ IS 'True if the aggregation of the deposit is currently blocked by some policy extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.';
+COMMENT ON COLUMN deposits.policy_details_serial_id
+ IS 'References policy extensions table, NULL if extensions are not used';
CREATE TABLE IF NOT EXISTS deposits_default
PARTITION OF deposits
@@ -591,7 +639,7 @@ CREATE OR REPLACE FUNCTION deposits_insert_trigger()
DECLARE
is_ready BOOLEAN;
BEGIN
- is_ready = NOT (NEW.done OR NEW.extension_blocked);
+ is_ready = NOT (NEW.done OR NEW.policy_blocked);
IF (is_ready)
THEN
@@ -635,8 +683,8 @@ DECLARE
DECLARE
is_ready BOOLEAN;
BEGIN
- was_ready = NOT (OLD.done OR OLD.extension_blocked);
- is_ready = NOT (NEW.done OR NEW.extension_blocked);
+ was_ready = NOT (OLD.done OR OLD.policy_blocked);
+ is_ready = NOT (NEW.done OR NEW.policy_blocked);
IF (was_ready AND NOT is_ready)
THEN
DELETE FROM exchange.deposits_by_ready
@@ -690,7 +738,7 @@ CREATE OR REPLACE FUNCTION deposits_delete_trigger()
DECLARE
was_ready BOOLEAN;
BEGIN
- was_ready = NOT (OLD.done OR OLD.extension_blocked);
+ was_ready = NOT (OLD.done OR OLD.policy_blocked);
IF (was_ready)
THEN
diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c
index 0ac70bae3..d6630797a 100644
--- a/src/exchangedb/pg_insert_records_by_table.c
+++ b/src/exchangedb/pg_insert_records_by_table.c
@@ -872,11 +872,11 @@ irbt_cb_table_deposits (struct PostgresClosure *pg,
GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.wire_salt),
GNUNET_PQ_query_param_auto_from_type (
&td->details.deposits.wire_target_h_payto),
- GNUNET_PQ_query_param_bool (td->details.deposits.extension_blocked),
- 0 == td->details.deposits.extension_details_serial_id
+ GNUNET_PQ_query_param_bool (td->details.deposits.policy_blocked),
+ 0 == td->details.deposits.policy_details_serial_id
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_uint64 (
- &td->details.deposits.extension_details_serial_id),
+ &td->details.deposits.policy_details_serial_id),
GNUNET_PQ_query_param_end
};
@@ -898,8 +898,8 @@ irbt_cb_table_deposits (struct PostgresClosure *pg,
",coin_sig"
",wire_salt"
",wire_target_h_payto"
- ",extension_blocked"
- ",extension_details_serial_id"
+ ",policy_blocked"
+ ",policy_details_serial_id"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
" $11, $12, $13, $14, $15, $16, $17);");
@@ -1217,9 +1217,9 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_string (td->details.extensions.name),
- NULL == td->details.extensions.config ?
+ NULL == td->details.extensions.manifest ?
GNUNET_PQ_query_param_null () :
- GNUNET_PQ_query_param_string (td->details.extensions.config),
+ GNUNET_PQ_query_param_string (td->details.extensions.manifest),
GNUNET_PQ_query_param_end
};
@@ -1228,7 +1228,7 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
"INSERT INTO extensions"
"(extension_id"
",name"
- ",config"
+ ",manifest"
") VALUES "
"($1, $2, $3);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
@@ -1238,34 +1238,99 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
/**
- * Function called with extension_details records to insert into table.
+ * Function called with policy_details records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
-irbt_cb_table_extension_details (struct PostgresClosure *pg,
- const struct TALER_EXCHANGEDB_TableData *td)
+irbt_cb_table_policy_details (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
- NULL ==
- td->details.extension_details.extension_options ?
- GNUNET_PQ_query_param_null () :
- GNUNET_PQ_query_param_string (
- td->details.extension_details.extension_options),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.policy_details.hash_code),
+ (td->details.policy_details.no_policy_json)
+ ? GNUNET_PQ_query_param_null ()
+ : TALER_PQ_query_param_json (td->details.policy_details.policy_json),
+ TALER_PQ_query_param_amount (&td->details.policy_details.commitment),
+ TALER_PQ_query_param_amount (&td->details.policy_details.accumulated_total),
+ TALER_PQ_query_param_amount (&td->details.policy_details.fee),
+ TALER_PQ_query_param_amount (&td->details.policy_details.transferable),
+ GNUNET_PQ_query_param_timestamp (&td->details.policy_details.deadline),
+ GNUNET_PQ_query_param_uint16 (
+ &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
};
PREPARE (pg,
- "insert_into_table_extension_details",
- "INSERT INTO extension_details"
- "(extension_details_serial_id"
- ",extension_options"
+ "insert_into_table_policy_details",
+ "INSERT INTO policy_details"
+ "(policy_details_serial_id"
+ ",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"
+ ",fulfillment_id"
") VALUES "
"($1, $2);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_into_table_extension_details",
+ "insert_into_table_policy_details",
+ params);
+}
+
+
+/**
+ * Function called with policy_fulfillment records to insert into table.
+ *
+ * @param pg plugin context
+ * @param td record to insert
+ */
+static enum GNUNET_DB_QueryStatus
+irbt_cb_table_policy_fulfillments (struct PostgresClosure *pg,
+ const struct TALER_EXCHANGEDB_TableData *td)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&td->serial),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.policy_fulfillments.fulfillment_timestamp),
+ (NULL == td->details.policy_fulfillments.fulfillment_proof)
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (
+ td->details.policy_fulfillments.fulfillment_proof),
+ GNUNET_PQ_query_param_auto_from_type (
+ &td->details.policy_fulfillments.h_fulfillment_proof),
+ GNUNET_PQ_query_param_fixed_size (
+ td->details.policy_fulfillments.policy_hash_codes,
+ td->details.policy_fulfillments.policy_hash_codes_count),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_into_table_policy_fulfillments",
+ "INSERT INTO policy_fulfillments "
+ "(fulfillment_id"
+ ",fulfillment_timestamp"
+ ",fulfillment_proof"
+ ",h_fulfillment_proof"
+ ",policy_hash_codes"
+ ") VALUES "
+ "($1, $2, $3, $4, $5);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_into_table_policy_fulfillments",
params);
}
@@ -1900,8 +1965,11 @@ TEH_PG_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_EXTENSIONS:
rh = &irbt_cb_table_extensions;
break;
- case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
- rh = &irbt_cb_table_extension_details;
+ case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
+ rh = &irbt_cb_table_policy_details;
+ break;
+ case TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS:
+ rh = &irbt_cb_table_policy_fulfillments;
break;
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
rh = &irbt_cb_table_purse_requests;
diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c
index 57c7c2b07..9d600f740 100644
--- a/src/exchangedb/pg_lookup_records_by_table.c
+++ b/src/exchangedb/pg_lookup_records_by_table.c
@@ -927,7 +927,7 @@ lrbt_cb_table_deposits (void *cls,
for (unsigned int i = 0; i<num_results; i++)
{
- bool no_extension;
+ bool no_policy;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
"serial",
@@ -972,13 +972,13 @@ lrbt_cb_table_deposits (void *cls,
"wire_target_h_payto",
&td.details.deposits.wire_target_h_payto),
GNUNET_PQ_result_spec_auto_from_type (
- "extension_blocked",
- &td.details.deposits.extension_blocked),
+ "policy_blocked",
+ &td.details.deposits.policy_blocked),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_uint64 (
- "extension_details_serial_id",
- &td.details.deposits.extension_details_serial_id),
- &no_extension),
+ "policy_details_serial_id",
+ &td.details.deposits.policy_details_serial_id),
+ &no_policy),
GNUNET_PQ_result_spec_end
};
@@ -1414,7 +1414,7 @@ lrbt_cb_table_extensions (void *cls,
struct TALER_EXCHANGEDB_TableData td = {
.table = TALER_EXCHANGEDB_RT_EXTENSIONS
};
- bool no_config = false;
+ bool no_manifest = false;
for (unsigned int i = 0; i<num_results; i++)
{
@@ -1424,9 +1424,9 @@ lrbt_cb_table_extensions (void *cls,
GNUNET_PQ_result_spec_string ("name",
&td.details.extensions.name),
GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_string ("config",
- &td.details.extensions.config),
- &no_config),
+ GNUNET_PQ_result_spec_string ("manifest",
+ &td.details.extensions.manifest),
+ &no_manifest),
GNUNET_PQ_result_spec_end
};
@@ -1447,33 +1447,112 @@ lrbt_cb_table_extensions (void *cls,
/**
- * Function called with extension_details table entries.
+ * Function called with policy_details 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_extension_details (void *cls,
- PGresult *result,
- unsigned int num_results)
+lrbt_cb_table_policy_details (void *cls,
+ PGresult *result,
+ 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++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
+ &td.serial),
+ GNUNET_PQ_result_spec_auto_from_type ("hash_code",
+ &td.details.policy_details.
+ hash_code),
+ GNUNET_PQ_result_spec_allow_null (
+ 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),
+ 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.
+ 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
+ };
+
+ 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 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_fulfillments (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
struct LookupRecordsByTableContext *ctx = cls;
struct TALER_EXCHANGEDB_TableData td = {
- .table = TALER_EXCHANGEDB_RT_EXTENSION_DETAILS
+ .table = TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS
};
for (unsigned int i = 0; i<num_results; i++)
{
- bool no_config = false;
+ bool no_proof = false;
+ bool no_timestamp = false;
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("extension_details_serial_id",
+ GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
&td.serial),
GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_string ("extension_options",
- &td.details.extension_details.
- extension_options),
- &no_config),
+ 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 ("fulfillment_proof",
+ &td.details.policy_fulfillments.
+ fulfillment_proof),
+ &no_proof),
GNUNET_PQ_result_spec_end
};
@@ -2607,9 +2686,13 @@ TEH_PG_lookup_records_by_table (void *cls,
statement = "select_above_serial_by_table_extensions";
rh = &lrbt_cb_table_extensions;
break;
- case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
- statement = "select_above_serial_by_table_extension_details";
- rh = &lrbt_cb_table_extension_details;
+ case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
+ statement = "select_above_serial_by_table_policy_details";
+ rh = &lrbt_cb_table_policy_details;
+ break;
+ case TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS:
+ statement = "select_above_serial_by_table_policy_fulfillments";
+ rh = &lrbt_cb_table_policy_fulfillments;
break;
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
XPREPARE ("select_above_serial_by_table_purse_requests",
diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c
index 202be30f8..7e150cd28 100644
--- a/src/exchangedb/pg_lookup_serial_by_table.c
+++ b/src/exchangedb/pg_lookup_serial_by_table.c
@@ -277,12 +277,20 @@ TEH_PG_lookup_serial_by_table (void *cls,
" ORDER BY extension_id DESC"
" LIMIT 1;");
break;
- case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
- XPREPARE ("select_serial_by_table_extension_details",
+ case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
+ XPREPARE ("select_serial_by_table_policy_details",
"SELECT"
- " extension_details_serial_id AS serial"
- " FROM extension_details"
- " ORDER BY extension_details_serial_id DESC"
+ " policy_details_serial_id AS serial"
+ " FROM policy_details"
+ " ORDER BY policy_details_serial_id DESC"
+ " LIMIT 1;");
+ break;
+ case TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS:
+ XPREPARE ("select_serial_by_table_policy_fulfillments",
+ "SELECT"
+ " fulfillment_id AS serial"
+ " FROM policy_fulfillments"
+ " ORDER BY fulfillment_id DESC"
" LIMIT 1;");
break;
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 28dbdbaa8..0b03fe4b1 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2099,17 +2099,17 @@ prepare_statements (struct PostgresClosure *pg)
" WHERE job_name=$1"
" AND start_row=$2"
" AND end_row=$3"),
- /* Used in #postgres_set_extension_config */
+ /* Used in #postgres_set_extension_manifest */
GNUNET_PQ_make_prepare (
- "set_extension_config",
- "INSERT INTO extensions (name, config) VALUES ($1, $2) "
+ "set_extension_manifest",
+ "INSERT INTO extensions (name, manifest) VALUES ($1, $2) "
"ON CONFLICT (name) "
- "DO UPDATE SET config=$2"),
- /* Used in #postgres_get_extension_config */
+ "DO UPDATE SET manifest=$2"),
+ /* Used in #postgres_get_extension_manifest */
GNUNET_PQ_make_prepare (
- "get_extension_config",
+ "get_extension_manifest",
"SELECT "
- " config "
+ " manifest "
"FROM extensions"
" WHERE name=$1;"),
/* Used in #postgres_insert_contract() */
@@ -4083,7 +4083,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 extension_blocked true if an extension is blocking the wire transfer
+ * @param _blocked true if an extension is blocking the wire transfer
* @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
@@ -4095,7 +4095,7 @@ postgres_do_deposit (
const struct TALER_EXCHANGEDB_Deposit *deposit,
uint64_t known_coin_id,
const struct TALER_PaytoHashP *h_payto,
- bool extension_blocked,
+ uint64_t *policy_details_serial_id,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
bool *in_conflict)
@@ -4117,10 +4117,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 (extension_blocked),
- (NULL == deposit->extension_details)
+ GNUNET_PQ_query_param_bool (deposit->has_policy),
+ (NULL == policy_details_serial_id)
? GNUNET_PQ_query_param_null ()
- : TALER_PQ_query_param_json (deposit->extension_details),
+ : GNUNET_PQ_query_param_uint64 (policy_details_serial_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -4140,6 +4140,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.
@@ -4394,6 +4489,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;
+}
+
+
/**
* Get the balance of the specified reserve.
*
@@ -10465,8 +10672,8 @@ postgres_delete_shard_locks (void *cls)
/**
- * Function called to save the configuration of an extension
- * (age-restriction, peer2peer, ...). After successful storage of the
+ * Function called to save the manifest of an extension
+ * (age-restriction, policy_extension_...) After successful storage of the
* configuration it triggers the corresponding event.
*
* @param cls the @e cls of this struct with the plugin-specific state
@@ -10475,15 +10682,15 @@ postgres_delete_shard_locks (void *cls)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
-postgres_set_extension_config (void *cls,
- const char *extension_name,
- const char *config)
+postgres_set_extension_manifest (void *cls,
+ const char *extension_name,
+ const char *manifest)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam pcfg =
- (NULL == config || 0 == *config)
+ (NULL == manifest || 0 == *manifest)
? GNUNET_PQ_query_param_null ()
- : GNUNET_PQ_query_param_string (config);
+ : GNUNET_PQ_query_param_string (manifest);
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (extension_name),
pcfg,
@@ -10491,24 +10698,24 @@ postgres_set_extension_config (void *cls,
};
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "set_extension_config",
+ "set_extension_manifest",
params);
}
/**
- * Function called to get the configuration of an extension
- * (age-restriction, peer2peer, ...)
+ * Function called to get the manifest of an extension
+ * (age-restriction, policy_extension_...)
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param[out] config JSON object of the configuration as string
+ * @param[out] manifest JSON object of the manifest as string
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
-postgres_get_extension_config (void *cls,
- const char *extension_name,
- char **config)
+postgres_get_extension_manifest (void *cls,
+ const char *extension_name,
+ char **manifest)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@@ -10518,15 +10725,15 @@ postgres_get_extension_config (void *cls,
bool is_null;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_string ("config",
- config),
+ GNUNET_PQ_result_spec_string ("manifest",
+ manifest),
&is_null),
GNUNET_PQ_result_spec_end
};
- *config = NULL;
+ *manifest = NULL;
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "get_extension_config",
+ "get_extension_manifest",
params,
rs);
}
@@ -12311,8 +12518,11 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->get_withdraw_info = &postgres_get_withdraw_info;
plugin->do_withdraw = &postgres_do_withdraw;
plugin->do_batch_withdraw = &postgres_do_batch_withdraw;
+ plugin->get_policy_details = &postgres_get_policy_details;
+ plugin->persist_policy_details = &postgres_persist_policy_details;
plugin->do_batch_withdraw_insert = &postgres_do_batch_withdraw_insert;
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;
@@ -12446,10 +12656,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_release_revolving_shard;
plugin->delete_shard_locks
= &postgres_delete_shard_locks;
- plugin->set_extension_config
- = &postgres_set_extension_config;
- plugin->get_extension_config
- = &postgres_get_extension_config;
+ plugin->set_extension_manifest
+ = &postgres_set_extension_manifest;
+ plugin->get_extension_manifest
+ = &postgres_get_extension_manifest;
plugin->insert_partner
= &postgres_insert_partner;
plugin->insert_contract
diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql
index e9075775c..30610e868 100644
--- a/src/exchangedb/procedures.sql
+++ b/src/exchangedb/procedures.sql
@@ -510,8 +510,8 @@ CREATE OR REPLACE FUNCTION exchange_do_deposit(
IN in_coin_pub BYTEA,
IN in_coin_sig BYTEA,
IN in_shard INT8,
- IN in_extension_blocked BOOLEAN,
- IN in_extension_details VARCHAR,
+ IN in_policy_blocked BOOLEAN,
+ IN in_policy_details_serial_id INT8,
OUT out_exchange_timestamp INT8,
OUT out_balance_ok BOOLEAN,
OUT out_conflict BOOLEAN)
@@ -519,26 +519,12 @@ LANGUAGE plpgsql
AS $$
DECLARE
wtsi INT8; -- wire target serial id
-DECLARE
- xdi INT8; -- eXstension details serial id
BEGIN
-- Shards: INSERT extension_details (by extension_details_serial_id)
-- 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_extension_details
-THEN
- INSERT INTO exchange.extension_details
- (extension_options)
- VALUES
- (in_extension_details)
- RETURNING extension_details_serial_id INTO xdi;
-ELSE
- xdi=NULL;
-END IF;
-
-
INSERT INTO exchange.wire_targets
(wire_target_h_payto
,payto_uri)
@@ -572,8 +558,8 @@ INSERT INTO exchange.deposits
,coin_sig
,wire_salt
,wire_target_h_payto
- ,extension_blocked
- ,extension_details_serial_id
+ ,policy_blocked
+ ,policy_details_serial_id
)
VALUES
(in_shard
@@ -590,8 +576,8 @@ INSERT INTO exchange.deposits
,in_coin_sig
,in_wire_salt
,in_h_payto
- ,in_extension_blocked
- ,xdi)
+ ,in_policy_blocked
+ ,in_policy_details_serial_id)
ON CONFLICT DO NOTHING;
IF NOT FOUND
@@ -611,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
@@ -2420,5 +2407,117 @@ RETURN;
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_commitment_val INT8;
+ cur_commitment_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)
+ ON CONFLICT (policy_hash_code) DO NOTHING
+ RETURNING policy_details_serial_id INTO out_policy_details_serial_id;
+
+ -- If the insert was successful, return
+ -- We assume that the fullfilment_state was correct in first place.
+ IF FOUND THEN
+ out_accumulated_total_val = in_accumulated_total_val;
+ out_accumulated_total_frac = in_accumulated_total_frac;
+ out_fulfillment_state = in_fulfillment_state;
+ 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 eb990412e..111ee9365 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -112,53 +112,53 @@ mark_prepare_cb (void *cls,
* Simple check that config retrieval and setting for extensions work
*/
static enum GNUNET_GenericReturnValue
-test_extension_config (void)
+test_extension_manifest (void)
{
- char *config;
+ char *manifest;
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
- plugin->get_extension_config (plugin->cls,
- "fnord",
- &config));
+ plugin->get_extension_manifest (plugin->cls,
+ "fnord",
+ &manifest));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->set_extension_config (plugin->cls,
- "fnord",
- "bar"));
+ plugin->set_extension_manifest (plugin->cls,
+ "fnord",
+ "bar"));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->get_extension_config (plugin->cls,
- "fnord",
- &config));
+ plugin->get_extension_manifest (plugin->cls,
+ "fnord",
+ &manifest));
- FAILIF (0 != strcmp ("bar", config));
- GNUNET_free (config);
+ FAILIF (0 != strcmp ("bar", manifest));
+ GNUNET_free (manifest);
/* let's do this again! */
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->set_extension_config (plugin->cls,
- "fnord",
- "buzz"));
+ plugin->set_extension_manifest (plugin->cls,
+ "fnord",
+ "buzz"));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->get_extension_config (plugin->cls,
- "fnord",
- &config));
+ plugin->get_extension_manifest (plugin->cls,
+ "fnord",
+ &manifest));
- FAILIF (0 != strcmp ("buzz", config));
- GNUNET_free (config);
+ FAILIF (0 != strcmp ("buzz", manifest));
+ GNUNET_free (manifest);
/* let's do this again, with NULL */
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->set_extension_config (plugin->cls,
- "fnord",
- NULL));
+ plugin->set_extension_manifest (plugin->cls,
+ "fnord",
+ NULL));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->get_extension_config (plugin->cls,
- "fnord",
- &config));
+ plugin->get_extension_manifest (plugin->cls,
+ "fnord",
+ &manifest));
- FAILIF (NULL != config);
+ FAILIF (NULL != manifest);
return GNUNET_OK;
drop:
@@ -1269,7 +1269,7 @@ run (void *cls)
NULL));
/* simple extension check */
FAILIF (GNUNET_OK !=
- test_extension_config ());
+ test_extension_manifest ());
RND_BLK (&reserve_pub);
GNUNET_assert (GNUNET_OK ==
diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am
index 5d4ed128e..c867a9512 100644
--- a/src/extensions/Makefile.am
+++ b/src/extensions/Makefile.am
@@ -11,7 +11,7 @@ if USE_COVERAGE
endif
-# Libraries
+# Basic extension handling library
lib_LTLIBRARIES = \
libtalerextensions.la
@@ -22,7 +22,7 @@ libtalerextensions_la_LDFLAGS = \
libtalerextensions_la_SOURCES = \
extensions.c \
- extension_age_restriction.c
+ age_restriction_helper.c
libtalerextensions_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@@ -31,3 +31,4 @@ libtalerextensions_la_LIBADD = \
-lgnunetutil \
-ljansson \
$(XLIB)
+
diff --git a/src/extensions/age_restriction/Makefile.am b/src/extensions/age_restriction/Makefile.am
new file mode 100644
index 000000000..85d676538
--- /dev/null
+++ b/src/extensions/age_restriction/Makefile.am
@@ -0,0 +1,33 @@
+# This Makefile.am is in the public domain
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ $(LIBGCRYPT_CFLAGS) \
+ $(POSTGRESQL_CPPFLAGS)
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+# Age restriction as extension library
+
+plugindir = $(libdir)/taler
+
+plugin_LTLIBRARIES = \
+ libtaler_extension_age_restriction.la
+
+libtaler_extension_age_restriction_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtaler_extension_age_restriction_la_SOURCES = \
+ age_restriction.c
+
+libtaler_extension_age_restriction_la_LIBADD = \
+ $(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetjson \
+ -lgnunetutil \
+ -ljansson \
+ $(XLIB)
diff --git a/src/extensions/age_restriction/age_restriction.c b/src/extensions/age_restriction/age_restriction.c
new file mode 100644
index 000000000..581242503
--- /dev/null
+++ b/src/extensions/age_restriction/age_restriction.c
@@ -0,0 +1,258 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021-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 age_restriction.c
+ * @brief Utility functions regarding age restriction
+ * @author Özgür Kesim
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+/* ==================================================
+ *
+ * Age Restriction TALER_Extension implementation
+ *
+ * ==================================================
+ */
+
+/**
+ * @brief local configuration
+ */
+
+static struct TALER_AgeRestrictionConfig AR_config = {0};
+
+/**
+ * @brief implements the TALER_Extension.disable interface.
+ *
+ * @param ext Pointer to the current extension
+ */
+static void
+age_restriction_disable (
+ struct TALER_Extension *ext)
+{
+ if (NULL == ext)
+ return;
+
+ ext->enabled = false;
+ ext->config = NULL;
+
+ AR_config.mask.bits = 0;
+ AR_config.num_groups = 0;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.load_config interface.
+ *
+ * @param ext if NULL, only tests the configuration
+ * @param jconfig the configuration as json
+ */
+static enum GNUNET_GenericReturnValue
+age_restriction_load_config (
+ struct TALER_Extension *ext,
+ json_t *jconfig)
+{
+ struct TALER_AgeMask mask = {0};
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = TALER_JSON_parse_age_groups (jconfig, &mask);
+ if (GNUNET_OK != ret)
+ return ret;
+
+ /* only testing the parser */
+ if (ext == NULL)
+ return GNUNET_OK;
+
+ if (TALER_Extension_AgeRestriction != ext->type)
+ return GNUNET_SYSERR;
+
+ if (mask.bits > 0)
+ {
+ /* if the mask is not zero, the first bit MUST be set */
+ if (0 == (mask.bits & 1))
+ return GNUNET_SYSERR;
+
+ AR_config.mask.bits = mask.bits;
+ AR_config.num_groups = __builtin_popcount (mask.bits) - 1;
+ }
+
+ ext->config = &AR_config;
+ ext->enabled = true;
+ json_decref (jconfig);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "loaded new age restriction config with age groups: %s\n",
+ TALER_age_mask_to_string (&mask));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.manifest interface.
+ *
+ * @param ext if NULL, only tests the configuration
+ * @return configuration as json_t* object, maybe NULL
+ */
+static json_t *
+age_restriction_manifest (
+ const struct TALER_Extension *ext)
+{
+ char *mask_str;
+ json_t *conf;
+
+ GNUNET_assert (NULL != ext);
+
+ if (NULL == ext->config)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "age restriction not configured");
+ return json_null ();
+ }
+
+ mask_str = TALER_age_mask_to_string (&AR_config.mask);
+ conf = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("age_groups", mask_str)
+ );
+
+ free (mask_str);
+
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_bool ("critical", ext->critical),
+ GNUNET_JSON_pack_string ("version", ext->version),
+ GNUNET_JSON_pack_object_steal ("config", conf)
+ );
+}
+
+
+/* The extension for age restriction */
+struct TALER_Extension TE_age_restriction = {
+ .type = TALER_Extension_AgeRestriction,
+ .name = "age_restriction",
+ .critical = false,
+ .version = "1",
+ .enabled = false, /* disabled per default */
+ .config = NULL,
+ .disable = &age_restriction_disable,
+ .load_config = &age_restriction_load_config,
+ .manifest = &age_restriction_manifest,
+
+ /* This extension is not a policy extension */
+ .create_policy_details = NULL,
+ .policy_get_handler = NULL,
+ .policy_post_handler = NULL,
+};
+
+
+/**
+ * @brief implements the init() function for GNUNET_PLUGIN_load
+ *
+ * @param arg Pointer to the GNUNET_CONFIGURATION_Handle
+ * @return pointer to TALER_Extension on success or NULL otherwise.
+ */
+void *
+libtaler_extension_age_restriction_init (void *arg)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
+ char *groups = NULL;
+ struct TALER_AgeMask mask = {0};
+
+ if ((GNUNET_YES !=
+ GNUNET_CONFIGURATION_have_value (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "ENABLED"))
+ ||
+ (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "ENABLED")))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "[age restriction] no section %s found in configuration\n",
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION);
+
+ return NULL;
+ }
+
+ /* Age restriction is enabled, extract age groups */
+ if ((GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "AGE_GROUPS"))
+ &&
+ (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION,
+ "AGE_GROUPS",
+ &groups)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "[age restriction] AGE_GROUPS in %s is not a string\n",
+ TALER_EXTENSION_SECTION_AGE_RESTRICTION);
+
+ return NULL;
+ }
+
+ mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
+
+ if ((groups != NULL) &&
+ (GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "[age restriction] couldn't parse age groups: '%s'\n",
+ groups);
+ return NULL;
+ }
+
+ AR_config.mask = mask;
+ AR_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "[age restriction] setting age mask to %s with #groups: %d\n",
+ TALER_age_mask_to_string (&AR_config.mask),
+ __builtin_popcount (AR_config.mask.bits) - 1);
+
+ TE_age_restriction.config = &AR_config;
+
+ /* Note: we do now have TE_age_restriction_config set, however the extension
+ * is not yet enabled! For age restriction to become active, load_config must
+ * have been called. */
+
+ GNUNET_free (groups);
+ return &TE_age_restriction;
+}
+
+
+/**
+ * @brief implements the done() function for GNUNET_PLUGIN_load
+ *
+ * @param cfg unsued
+ * @return pointer to TALER_Extension on success or NULL otherwise.
+ */
+void *
+libtaler_extension_age_restriction_done (void *arg)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "[age restriction] disabling and unloading");
+ AR_config.mask.bits = 0;
+ AR_config.num_groups = 0;
+ return NULL;
+}
+
+
+/* end of age_restriction.c */
diff --git a/src/extensions/age_restriction_helper.c b/src/extensions/age_restriction_helper.c
new file mode 100644
index 000000000..8ba835117
--- /dev/null
+++ b/src/extensions/age_restriction_helper.c
@@ -0,0 +1,73 @@
+/*
+ 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 age_restriction_helper.c
+ * @brief Helper functions for age restriction
+ * @author Özgür Kesim
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_extensions.h"
+#include "stdint.h"
+
+
+const struct TALER_AgeRestrictionConfig *
+TALER_extensions_get_age_restriction_config ()
+{
+ const struct TALER_Extension *ext;
+
+ ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+ if (NULL == ext)
+ return NULL;
+
+ return ext->config;
+}
+
+
+bool
+TALER_extensions_is_age_restriction_enabled ()
+{
+ const struct TALER_Extension *ext;
+
+ ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+ if (NULL == ext)
+ return false;
+
+ return ext->enabled;
+}
+
+
+struct TALER_AgeMask
+TALER_extensions_get_age_restriction_mask ()
+{
+ const struct TALER_Extension *ext;
+ const struct TALER_AgeRestrictionConfig *conf;
+
+ ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+
+ if ((NULL == ext) ||
+ (NULL == ext->config))
+ return (struct TALER_AgeMask) {0}
+ ;
+
+ conf = ext->config;
+ return conf->mask;
+}
+
+
+/* end age_restriction_helper.c */
diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/extension_age_restriction.c
deleted file mode 100644
index 00a038415..000000000
--- a/src/extensions/extension_age_restriction.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2021-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 extension_age_restriction.c
- * @brief Utility functions regarding age restriction
- * @author Özgür Kesim
- */
-#include "platform.h"
-#include "taler_util.h"
-#include "taler_extensions.h"
-#include "stdint.h"
-
-/**
- * Carries all the information we need for age restriction
- */
-struct age_restriction_config
-{
- struct TALER_AgeMask mask;
- size_t num_groups;
-};
-
-/**
- * Global config for this extension
- */
-static struct age_restriction_config TE_age_restriction_config = {0};
-
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (
- const char *groups,
- struct TALER_AgeMask *mask)
-{
-
- const char *pos = groups;
- unsigned int prev = 0;
- unsigned int val = 0;
- char c;
-
- while (*pos)
- {
- c = *pos++;
- if (':' == c)
- {
- if (prev >= val)
- return GNUNET_SYSERR;
-
- mask->bits |= 1 << val;
- prev = val;
- val = 0;
- continue;
- }
-
- if ('0'>c || '9'<c)
- return GNUNET_SYSERR;
-
- val = 10 * val + c - '0';
-
- if (0>=val || 32<=val)
- return GNUNET_SYSERR;
- }
-
- if (32<=val || prev>=val)
- return GNUNET_SYSERR;
-
- mask->bits |= (1 << val);
- mask->bits |= 1; // mark zeroth group, too
-
- return GNUNET_OK;
-}
-
-
-char *
-TALER_age_mask_to_string (
- const struct TALER_AgeMask *mask)
-{
- uint32_t bits = mask->bits;
- unsigned int n = 0;
- char *buf = GNUNET_malloc (32 * 3); // max characters possible
- char *pos = buf;
-
- if (NULL == buf)
- {
- return buf;
- }
-
- while (bits != 0)
- {
- bits >>= 1;
- n++;
- if (0 == (bits & 1))
- {
- continue;
- }
-
- if (n > 9)
- {
- *(pos++) = '0' + n / 10;
- }
- *(pos++) = '0' + n % 10;
-
- if (0 != (bits >> 1))
- {
- *(pos++) = ':';
- }
- }
- return buf;
-}
-
-
-/* ==================================================
- *
- * Age Restriction TALER_Extension implementation
- *
- * ==================================================
- */
-
-/**
- * @brief implements the TALER_Extension.disable interface.
- *
- * @param ext Pointer to the current extension
- */
-static void
-age_restriction_disable (
- struct TALER_Extension *ext)
-{
- if (NULL == ext)
- return;
-
- ext->config = NULL;
-
- if (NULL != ext->config_json)
- {
- json_decref (ext->config_json);
- ext->config_json = NULL;
- }
-
- TE_age_restriction_config.mask.bits = 0;
- TE_age_restriction_config.num_groups = 0;
-}
-
-
-/**
- * @brief implements the TALER_Extension.load_taler_config interface.
- *
- * @param ext Pointer to the current extension
- * @param cfg Handle to the GNUNET configuration
- * @return Error if extension for age restriction was set, but age groups were
- * invalid, OK otherwise.
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_load_taler_config (
- struct TALER_Extension *ext,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- char *groups = NULL;
- enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
- struct TALER_AgeMask mask = {0};
-
- if ((GNUNET_YES !=
- GNUNET_CONFIGURATION_have_value (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "ENABLED"))
- ||
- (GNUNET_YES !=
- GNUNET_CONFIGURATION_get_value_yesno (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "ENABLED")))
- {
- /* Age restriction is not enabled */
- ext->config = NULL;
- ext->config_json = NULL;
- return GNUNET_OK;
- }
-
- /* Age restriction is enabled, extract age groups */
- if ((GNUNET_YES ==
- GNUNET_CONFIGURATION_have_value (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "AGE_GROUPS"))
- &&
- (GNUNET_YES !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- TALER_EXTENSION_SECTION_AGE_RESTRICTION,
- "AGE_GROUPS",
- &groups)))
- return GNUNET_SYSERR;
-
-
- mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
- ret = GNUNET_OK;
-
- if (groups != NULL)
- {
- ret = TALER_parse_age_group_string (groups, &mask);
- if (GNUNET_OK != ret)
- mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
- }
-
- if (GNUNET_OK == ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "setting age mask to %x with #groups: %d\n", mask.bits,
- __builtin_popcount (mask.bits) - 1);
- TE_age_restriction_config.mask.bits = mask.bits;
- TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
- ext->config = &TE_age_restriction_config;
-
- /* Note: we do now have TE_age_restriction_config set, however
- * ext->config_json is NOT set, i.e. the extension is not yet active! For
- * age restriction to become active, load_json_config must have been
- * called. */
- }
-
-
- GNUNET_free (groups);
- return ret;
-}
-
-
-/**
- * @brief implements the TALER_Extension.load_json_config interface.
- *
- * @param ext if NULL, only tests the configuration
- * @param jconfig the configuration as json
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_load_json_config (
- struct TALER_Extension *ext,
- json_t *jconfig)
-{
- struct TALER_AgeMask mask = {0};
- enum GNUNET_GenericReturnValue ret;
-
- ret = TALER_JSON_parse_age_groups (jconfig, &mask);
- if (GNUNET_OK != ret)
- return ret;
-
- /* only testing the parser */
- if (ext == NULL)
- return GNUNET_OK;
-
- if (TALER_Extension_AgeRestriction != ext->type)
- return GNUNET_SYSERR;
-
- TE_age_restriction_config.mask.bits = mask.bits;
- TE_age_restriction_config.num_groups = 0;
-
- if (mask.bits > 0)
- {
- /* if the mask is not zero, the first bit MUST be set */
- if (0 == (mask.bits & 1))
- return GNUNET_SYSERR;
-
- TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1;
- }
-
- ext->config = &TE_age_restriction_config;
-
- if (NULL != ext->config_json)
- json_decref (ext->config_json);
-
- ext->config_json = jconfig;
-
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "loaded new age restriction config with age groups: %s\n",
- TALER_age_mask_to_string (&mask));
-
- return GNUNET_OK;
-}
-
-
-/**
- * @brief implements the TALER_Extension.config_to_json interface.
- *
- * @param ext if NULL, only tests the configuration
- * @return configuration as json_t* object, maybe NULL
- */
-static json_t *
-age_restriction_config_to_json (
- const struct TALER_Extension *ext)
-{
- char *mask_str;
- json_t *conf;
-
- GNUNET_assert (NULL != ext);
-
- if (NULL == ext->config)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "age restriction not configured");
- return json_null ();
- }
-
- if (NULL != ext->config_json)
- {
- return json_copy (ext->config_json);
- }
-
- mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask);
- conf = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("age_groups", mask_str)
- );
-
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_bool ("critical", ext->critical),
- GNUNET_JSON_pack_string ("version", ext->version),
- GNUNET_JSON_pack_object_steal ("config", conf)
- );
-}
-
-
-/**
- * @brief implements the TALER_Extension.test_json_config interface.
- *
- * @param config configuration as json_t* to test
- * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise.
- */
-static enum GNUNET_GenericReturnValue
-age_restriction_test_json_config (
- const json_t *config)
-{
- struct TALER_AgeMask mask = {0};
-
- return TALER_JSON_parse_age_groups (config, &mask);
-}
-
-
-/* The extension for age restriction */
-struct TALER_Extension TE_age_restriction = {
- .next = NULL,
- .type = TALER_Extension_AgeRestriction,
- .name = "age_restriction",
- .critical = false,
- .version = "1",
- .config = NULL, // disabled per default
- .config_json = NULL,
- .disable = &age_restriction_disable,
- .test_json_config = &age_restriction_test_json_config,
- .load_json_config = &age_restriction_load_json_config,
- .config_to_json = &age_restriction_config_to_json,
- .load_taler_config = &age_restriction_load_taler_config,
-};
-
-enum GNUNET_GenericReturnValue
-TALER_extension_age_restriction_register ()
-{
- return TALER_extensions_add (&TE_age_restriction);
-}
-
-
-bool
-TALER_extensions_age_restriction_is_configured ()
-{
- return (0 != TE_age_restriction_config.mask.bits);
-}
-
-
-struct TALER_AgeMask
-TALER_extensions_age_restriction_ageMask ()
-{
- return TE_age_restriction_config.mask;
-}
-
-
-size_t
-TALER_extensions_age_restriction_num_groups ()
-{
- return TE_age_restriction_config.num_groups;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
- struct TALER_AgeMask *mask)
-{
- enum GNUNET_GenericReturnValue ret;
- const char *str;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("age_groups",
- &str),
- GNUNET_JSON_spec_end ()
- };
-
- ret = GNUNET_JSON_parse (root,
- spec,
- NULL,
- NULL);
- if (GNUNET_OK == ret)
- TALER_parse_age_group_string (str, mask);
-
- GNUNET_JSON_parse_free (spec);
-
- return ret;
-}
-
-
-/* end of extension_age_restriction.c */
diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c
index 0df0bae36..2ed973d92 100644
--- a/src/extensions/extensions.c
+++ b/src/extensions/extensions.c
@@ -24,51 +24,53 @@
#include "taler_extensions.h"
#include "stdint.h"
-
/* head of the list of all registered extensions */
-static struct TALER_Extension *TE_extensions = NULL;
-
+static struct TALER_Extensions TE_extensions = {
+ .next = NULL,
+ .extension = NULL,
+};
-const struct TALER_Extension *
+const struct TALER_Extensions *
TALER_extensions_get_head ()
{
- return TE_extensions;
+ return &TE_extensions;
}
-enum GNUNET_GenericReturnValue
-TALER_extensions_add (
- struct TALER_Extension *extension)
+static enum GNUNET_GenericReturnValue
+add_extension (
+ const struct TALER_Extension *extension)
{
/* Sanity checks */
if ((NULL == extension) ||
(NULL == extension->name) ||
(NULL == extension->version) ||
(NULL == extension->disable) ||
- (NULL == extension->test_json_config) ||
- (NULL == extension->load_json_config) ||
- (NULL == extension->config_to_json) ||
- (NULL == extension->load_taler_config))
+ (NULL == extension->load_config) ||
+ (NULL == extension->manifest))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"invalid extension\n");
return GNUNET_SYSERR;
}
- if (NULL == TE_extensions) /* first extension ?*/
- TE_extensions = (struct TALER_Extension *) extension;
+ if (NULL == TE_extensions.extension) /* first extension ?*/
+ TE_extensions.extension = extension;
else
{
- struct TALER_Extension *iter;
- struct TALER_Extension *last;
+ struct TALER_Extensions *iter;
+ struct TALER_Extensions *last;
/* Check for collisions */
- for (iter = TE_extensions; NULL != iter; iter = iter->next)
+ for (iter = &TE_extensions;
+ NULL != iter && NULL != iter->extension;
+ iter = iter->next)
{
+ const struct TALER_Extension *ext = iter->extension;
last = iter;
- if (extension->type == iter->type ||
+ if (extension->type == ext->type ||
0 == strcasecmp (extension->name,
- iter->name))
+ ext->name))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"extension collision for `%s'\n",
@@ -78,7 +80,11 @@ TALER_extensions_add (
}
/* No collisions found, so add this extension to the list */
- last->next = extension;
+ {
+ struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions);
+ extn->extension = extension;
+ last->next = extn;
+ }
}
return GNUNET_OK;
@@ -89,12 +95,12 @@ const struct TALER_Extension *
TALER_extensions_get_by_type (
enum TALER_Extension_Type type)
{
- for (const struct TALER_Extension *it = TE_extensions;
- NULL != it;
+ for (const struct TALER_Extensions *it = &TE_extensions;
+ NULL != it && NULL != it->extension;
it = it->next)
{
- if (it->type == type)
- return it;
+ if (it->extension->type == type)
+ return it->extension;
}
/* No extension found. */
@@ -109,8 +115,7 @@ TALER_extensions_is_enabled_type (
const struct TALER_Extension *ext =
TALER_extensions_get_by_type (type);
- return (NULL != ext &&
- TALER_extensions_is_enabled (ext));
+ return (NULL != ext && ext->enabled);
}
@@ -118,33 +123,34 @@ const struct TALER_Extension *
TALER_extensions_get_by_name (
const char *name)
{
- for (const struct TALER_Extension *it = TE_extensions;
+ for (const struct TALER_Extensions *it = &TE_extensions;
NULL != it;
it = it->next)
{
- if (0 == strcasecmp (name, it->name))
- return it;
+ if (0 == strcasecmp (name, it->extension->name))
+ return it->extension;
}
- /* No extension found. */
+ /* No extension found, try to load it. */
+
return NULL;
}
enum GNUNET_GenericReturnValue
-TALER_extensions_verify_json_config_signature (
- json_t *extensions,
+TALER_extensions_verify_manifests_signature (
+ json_t *manifests,
struct TALER_MasterSignatureP *extensions_sig,
struct TALER_MasterPublicKeyP *master_pub)
{
- struct TALER_ExtensionConfigHashP h_config;
+ struct TALER_ExtensionManifestsHashP h_manifests;
if (GNUNET_OK !=
- TALER_JSON_extensions_config_hash (extensions,
- &h_config))
+ TALER_JSON_extensions_manifests_hash (manifests,
+ &h_manifests))
return GNUNET_SYSERR;
if (GNUNET_OK !=
- TALER_exchange_offline_extension_config_hash_verify (
- &h_config,
+ TALER_exchange_offline_extension_manifests_hash_verify (
+ &h_manifests,
master_pub,
extensions_sig))
return GNUNET_NO;
@@ -178,7 +184,8 @@ configure_extension (
{
struct LoadConfClosure *col = cls;
const char *name;
- const struct TALER_Extension *extension;
+ char *lib_name;
+ struct TALER_Extension *extension;
if (GNUNET_OK != col->error)
return;
@@ -190,33 +197,49 @@ configure_extension (
name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
- if (NULL ==
- (extension = TALER_extensions_get_by_name (name)))
+
+ /* Load the extension library */
+ GNUNET_asprintf (&lib_name,
+ "libtaler_extension_%s",
+ name);
+ extension = GNUNET_PLUGIN_load (
+ lib_name,
+ (void *) col->cfg);
+ if (NULL == extension)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unsupported extension `%s` (section [%s]).\n", name,
+ "Couldn't load extension library to `%s` (section [%s]).\n",
+ name,
section);
col->error = GNUNET_SYSERR;
return;
}
- if (GNUNET_OK !=
- extension->load_taler_config (
- (struct TALER_Extension *) extension,
- col->cfg))
+
+ if (GNUNET_OK != add_extension (extension))
{
+ /* TODO: Ignoring return values here */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Couldn't parse configuration for extension `%s` (section [%s]).\n",
+ "Couldn't add extension `%s` (section [%s]).\n",
name,
section);
col->error = GNUNET_SYSERR;
+ GNUNET_PLUGIN_unload (
+ lib_name,
+ (void *) col->cfg);
return;
}
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "extension library '%s' loaded\n",
+ lib_name);
}
+static bool extensions_loaded = false;
+
enum GNUNET_GenericReturnValue
-TALER_extensions_load_taler_config (
+TALER_extensions_init (
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
struct LoadConfClosure col = {
@@ -224,15 +247,22 @@ TALER_extensions_load_taler_config (
.error = GNUNET_OK,
};
+ if (extensions_loaded)
+ return GNUNET_OK;
+
GNUNET_CONFIGURATION_iterate_sections (cfg,
&configure_extension,
&col);
+
+ if (GNUNET_OK == col.error)
+ extensions_loaded = true;
+
return col.error;
}
enum GNUNET_GenericReturnValue
-TALER_extensions_is_json_config (
+TALER_extensions_parse_manifest (
json_t *obj,
int *critical,
const char **version,
@@ -265,22 +295,23 @@ TALER_extensions_is_json_config (
enum GNUNET_GenericReturnValue
-TALER_extensions_load_json_config (
+TALER_extensions_load_manifests (
json_t *extensions)
{
const char*name;
- json_t *blob;
+ json_t *manifest;
GNUNET_assert (NULL != extensions);
GNUNET_assert (json_is_object (extensions));
- json_object_foreach (extensions, name, blob)
+ json_object_foreach (extensions, name, manifest)
{
int critical;
const char *version;
json_t *config;
- const struct TALER_Extension *extension =
- TALER_extensions_get_by_name (name);
+ struct TALER_Extension *extension = (struct
+ TALER_Extension *)
+ TALER_extensions_get_by_name (name);
if (NULL == extension)
{
@@ -291,45 +322,109 @@ TALER_extensions_load_json_config (
/* load and verify criticality, version, etc. */
if (GNUNET_OK !=
- TALER_extensions_is_json_config (
- blob, &critical, &version, &config))
+ TALER_extensions_parse_manifest (
+ manifest, &critical, &version, &config))
return GNUNET_SYSERR;
if (critical != extension->critical
|| 0 != strcmp (version, extension->version) // TODO: libtool compare?
|| NULL == config
- || GNUNET_OK != extension->test_json_config (config))
+ || GNUNET_OK != extension->load_config (NULL, config))
return GNUNET_SYSERR;
/* This _should_ work now */
if (GNUNET_OK !=
- extension->load_json_config ((struct TALER_Extension *) extension,
- config))
+ extension->load_config (extension, config))
return GNUNET_SYSERR;
+
+ extension->enabled = true;
}
/* make sure to disable all extensions that weren't mentioned in the json */
- for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+ for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
NULL != it;
it = it->next)
{
- if (NULL == json_object_get (extensions, it->name))
- it->disable ((struct TALER_Extension *) it);
+ if (NULL == json_object_get (extensions, it->extension->name))
+ it->extension->disable ((struct TALER_Extension *) it);
}
return GNUNET_OK;
}
-bool
-TALER_extensions_age_restriction_is_enabled ()
+/*
+ * Policy related
+ */
+
+static char *fulfillment2str[] = {
+ [TALER_PolicyFulfillmentReady] = "Ready",
+ [TALER_PolicyFulfillmentSuccess] = "Success",
+ [TALER_PolicyFulfillmentFailure] = "Failure",
+ [TALER_PolicyFulfillmentTimeout] = "Timeout",
+ [TALER_PolicyFulfillmentInsufficient] = "Insufficient",
+};
+
+const char *
+TALER_policy_fulfillment_state_str (
+ enum TALER_PolicyFulfillmentState state)
{
- const struct TALER_Extension *age =
- TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
+ GNUNET_assert (TALER_PolicyFulfillmentStateCount > state);
+ return fulfillment2str[state];
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_extensions_create_policy_details (
+ const json_t *policy_options,
+ struct TALER_PolicyDetails *details,
+ const char **error_hint)
+{
+ enum GNUNET_GenericReturnValue ret;
+ const struct TALER_Extension *extension;
+ const json_t *jtype;
+ const char *type;
+
+ *error_hint = NULL;
+
+ if ((NULL == policy_options) ||
+ (! json_is_object (policy_options)))
+ {
+ *error_hint = "invalid policy object";
+ return GNUNET_SYSERR;
+ }
+
+ jtype = json_object_get (policy_options, "type");
+ if (NULL == jtype)
+ {
+ *error_hint = "no type in policy object";
+ return GNUNET_SYSERR;
+ }
+
+ type = json_string_value (jtype);
+ if (NULL == type)
+ {
+ *error_hint = "invalid type in policy object";
+ return GNUNET_SYSERR;
+ }
+
+ extension = TALER_extensions_get_by_name (type);
+ if ((NULL == extension) ||
+ (NULL == extension->create_policy_details))
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unsupported extension policy '%s' requested\n",
+ type);
+ return GNUNET_NO;
+ }
+
+ details->deadline = GNUNET_TIME_UNIT_FOREVER_TS;
+ ret = extension->create_policy_details (policy_options,
+ details,
+ error_hint);
+ return ret;
- return (NULL != age &&
- NULL != age->config_json &&
- TALER_extensions_age_restriction_is_configured ());
}
diff --git a/src/include/taler_auditor_service.h b/src/include/taler_auditor_service.h
index 30d18e6e9..c20b789cc 100644
--- a/src/include/taler_auditor_service.h
+++ b/src/include/taler_auditor_service.h
@@ -233,7 +233,7 @@ typedef void
*
* @param auditor the auditor handle; the auditor must be ready to operate
* @param h_wire hash of merchant wire details
- * @param h_extensions hash over the extensions, if any
+ * @param h_policy hash over the policy, if any
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param wire_deadline date until which the exchange should wire the funds
@@ -257,7 +257,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle *
TALER_AUDITOR_deposit_confirmation (
struct TALER_AUDITOR_Handle *auditor,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_PrivateContractHashP *h_contract_terms,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h
index 9f46004c1..cf27668b6 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -382,9 +382,9 @@ struct TALER_AUDITORDB_DepositConfirmation
struct TALER_PrivateContractHashP h_contract_terms;
/**
- * Hash over the extensions for the deposit.
+ * Hash over the policy extension for the deposit.
*/
- struct TALER_ExtensionContractHashP h_extensions;
+ struct TALER_ExtensionPolicyHashP h_policy;
/**
* Hash over the wiring information of the merchant.
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 4c478cefd..6b8000933 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -638,10 +638,9 @@ struct TALER_PrivateContractHashP
/**
- * Hash used to represent the "public" extensions to
- * a contract that is shared with the exchange.
+ * Hash used to represent the policy extension to a deposit
*/
-struct TALER_ExtensionContractHashP
+struct TALER_ExtensionPolicyHashP
{
/**
* Actual hash value.
@@ -727,10 +726,10 @@ struct TALER_PickupIdentifierP
/**
- * @brief Salted hash over the JSON object representing the configuration of an
- * extension.
+ * @brief Salted hash over the JSON object representing the manifests of
+ * extensions.
*/
-struct TALER_ExtensionConfigHashP
+struct TALER_ExtensionManifestsHashP
{
/**
* Actual hash value.
@@ -3213,7 +3212,7 @@ TALER_wallet_reserve_attest_request_verify (
* @param h_wire hash of the merchant’s account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment, if applicable to the denomination (maybe NULL)
- * @param h_extensions hash over the extensions
+ * @param h_policy hash over the policy extension
* @param h_denom_pub hash of the coin denomination's public key
* @param coin_priv coin’s private key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
@@ -3228,7 +3227,7 @@ TALER_wallet_deposit_sign (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_DenominationHashP *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -3245,7 +3244,7 @@ TALER_wallet_deposit_sign (
* @param h_wire hash of the merchant’s account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment (maybe all zeroes, if not applicable to the denomination)
- * @param h_extensions hash over the extensions
+ * @param h_policy hash over the policy extension
* @param h_denom_pub hash of the coin denomination's public key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
@@ -3261,7 +3260,7 @@ TALER_wallet_deposit_verify (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_DenominationHashP *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -3666,7 +3665,7 @@ typedef enum TALER_ErrorCode
* @param scb function to call to create the signature
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_wire hash of the merchant’s account details
- * @param h_extensions hash over the extensions, can be NULL
+ * @param h_policy hash over the policy extension, can be NULL
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far off
* @param wire_deadline date until which the exchange should wire the funds
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
@@ -3682,7 +3681,7 @@ TALER_exchange_online_deposit_confirmation_sign (
TALER_ExchangeSignCallback scb,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -3698,7 +3697,7 @@ TALER_exchange_online_deposit_confirmation_sign (
*
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_wire hash of the merchant’s account details
- * @param h_extensions hash over the extensions, can be NULL
+ * @param h_policy hash over the policy extension, can be NULL
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far off
* @param wire_deadline date until which the exchange should wire the funds
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
@@ -3713,7 +3712,7 @@ enum GNUNET_GenericReturnValue
TALER_exchange_online_deposit_confirmation_verify (
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -5257,31 +5256,31 @@ TALER_merchant_contract_sign (
/* **************** /management/extensions offline signing **************** */
/**
- * Create a signature for the hash of the configuration of an extension
+ * Create a signature for the hash of the manifests of extensions
*
- * @param h_config hash of the JSON object representing the configuration
+ * @param h_manifests hash of the JSON object representing the manifests
* @param master_priv private key to sign with
* @param[out] master_sig where to write the signature
*/
void
-TALER_exchange_offline_extension_config_hash_sign (
- const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_sign (
+ const struct TALER_ExtensionManifestsHashP *h_manifests,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
/**
* Verify the signature in @a master_sig of the given hash, taken over the JSON
- * blob representing the configuration of an extension
+ * blob representing the manifests of extensions
*
- * @param h_config hash of the JSON blob of a configuration of an extension
+ * @param h_manifest hash of the JSON blob of manifests of extensions
* @param master_pub master public key of the exchange
* @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid
*/
enum GNUNET_GenericReturnValue
-TALER_exchange_offline_extension_config_hash_verify (
- const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_verify (
+ const struct TALER_ExtensionManifestsHashP *h_manifest,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
);
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index aa9a5b607..e3a349a92 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -894,9 +894,9 @@ struct TALER_EXCHANGE_DepositContractDetail
struct TALER_PrivateContractHashP h_contract_terms;
/**
- * Extension-specific details about the deposit relevant to the exchange.
+ * Policy extension specific details about the deposit relevant to the exchange.
*/
- const json_t *extension_details;
+ json_t *policy_details;
/**
* Timestamp when the contract was finalized, must match approximately the
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 9640e052f..f21301e7e 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"
/**
@@ -220,7 +221,8 @@ enum TALER_EXCHANGEDB_ReplicatedTable
TALER_EXCHANGEDB_RT_RECOUP,
TALER_EXCHANGEDB_RT_RECOUP_REFRESH,
TALER_EXCHANGEDB_RT_EXTENSIONS,
- TALER_EXCHANGEDB_RT_EXTENSION_DETAILS,
+ TALER_EXCHANGEDB_RT_POLICY_DETAILS,
+ TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS,
TALER_EXCHANGEDB_RT_PURSE_REQUESTS,
TALER_EXCHANGEDB_RT_PURSE_DECISION,
TALER_EXCHANGEDB_RT_PURSE_MERGES,
@@ -438,8 +440,8 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_WireSaltP wire_salt;
struct TALER_PaytoHashP wire_target_h_payto;
- bool extension_blocked;
- uint64_t extension_details_serial_id;
+ bool policy_blocked;
+ uint64_t policy_details_serial_id;
} deposits;
struct
@@ -510,13 +512,32 @@ struct TALER_EXCHANGEDB_TableData
struct
{
char *name;
- char *config;
+ char *manifest;
} extensions;
struct
{
- char *extension_options;
- } extension_details;
+ struct GNUNET_HashCode hash_code;
+ json_t *policy_json;
+ bool no_policy_json;
+ struct GNUNET_TIME_Timestamp deadline;
+ 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_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
{
@@ -1512,12 +1533,6 @@ struct TALER_EXCHANGEDB_Deposit
char *receiver_wire_account;
/**
- * Additional details for extensions relevant for this
- * deposit operation, possibly NULL!
- */
- json_t *extension_details;
-
- /**
* 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
@@ -1558,6 +1573,16 @@ struct TALER_EXCHANGEDB_Deposit
*/
struct TALER_Amount deposit_fee;
+ /*
+ * True if @e policy_json was provided
+ */
+ bool has_policy;
+
+ /**
+ * 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;
};
@@ -1656,6 +1681,17 @@ struct TALER_EXCHANGEDB_DepositListEntry
*/
struct TALER_Amount deposit_fee;
+ /*
+ * True if a policy was provided with the deposit request
+ */
+ bool has_policy;
+
+ /**
+ * 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;
+
/**
* Has the deposit been wired?
*/
@@ -3530,6 +3566,40 @@ 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
@@ -3539,7 +3609,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 extension_blocked true if an extension is blocking the wire transfer
+ * @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
@@ -3551,7 +3621,7 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_EXCHANGEDB_Deposit *deposit,
uint64_t known_coin_id,
const struct TALER_PaytoHashP *h_payto,
- bool extension_blocked,
+ uint64_t *policy_details_serial_id,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
bool *in_conflict);
@@ -3581,6 +3651,19 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * 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
* created CS signatures for the given @a nonce and @a new_denom_pub_hashes,
* and if so, return them in @a s_scalars. Otherwise, persist the
@@ -5559,33 +5642,33 @@ struct TALER_EXCHANGEDB_Plugin
/**
- * Function called to save the configuration of an extension
- * (age-restriction, peer2peer, ...)
+ * Function called to save the manifest of an extension
+ * (age-restriction, policy-extension, ...)
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param config JSON object of the configuration as string, maybe NULL (== disabled extension)
+ * @param manifest JSON object of the Manifest as string, maybe NULL (== disabled extension)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
- (*set_extension_config)(void *cls,
- const char *extension_name,
- const char *config);
+ (*set_extension_manifest)(void *cls,
+ const char *extension_name,
+ const char *manifest);
/**
- * Function called to retrieve the configuration of an extension
- * (age-restriction, peer2peer, ...)
+ * Function called to retrieve the manifest of an extension
+ * (age-restriction, policy-extension, ...)
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param[out] config JSON object of the configuration as string, maybe NULL (== disabled extension)
+ * @param[out] manifest Manifest of the extension in JSON encoding, maybe NULL (== disabled extension)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
- (*get_extension_config)(void *cls,
- const char *extension_name,
- char **config);
+ (*get_extension_manifest)(void *cls,
+ const char *extension_name,
+ char **manifest);
/**
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 8e1823cce..5e53d27f8 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
@@ -24,48 +24,195 @@
#include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h"
#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_extensions_policy.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
enum TALER_Extension_Type
{
- TALER_Extension_AgeRestriction = 0,
- TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
+ TALER_Extension_PolicyNull = 0,
+
+ TALER_Extension_AgeRestriction = 1,
+ TALER_Extension_PolicyMerchantRefund = 2,
+ TALER_Extension_PolicyBrandtVickeryAuction = 3,
+ TALER_Extension_PolicyEscrowedPayment = 4,
+
+ TALER_Extension_MaxPredefined = 5 // Must be last of the predefined
};
+
+/* Forward declarations */
+enum TALER_PolicyFulfillmentState;
+struct TALER_PolicyFulfillmentOutcome;
+
/*
* @brief Represents the implementation of an extension.
*
- * TODO: add documentation
+ * An "Extension" is an optional feature for the Exchange.
+ * There are only two types of extensions:
+ *
+ * a) Age restriction: This is a special feature that directly interacts with
+ * denominations and coins, but is not define policies during deposits, see b).
+ * The implementation of this extension doesn't have to implement any of the
+ * http- or depost-handlers in the struct.
+ *
+ * b) Policies for deposits: These are extensions that define policies (such
+ * as refund, escrow or auctions) for deposit requests. These extensions have
+ * to implement at least the deposit- and post-http-handler in the struct to be
+ * functional.
+ *
+ * In addition to the handlers defined in this struct, an extension must also
+ * be a plugin in the GNUNET_Plugin sense. That is, it must implement the
+ * functions
+ * 1: (void *ext)libtaler_extension_<name>_init(void *cfg)
+ * and
+ * 2: (void *)libtaler_extension_<name>_done(void *)
+ *
+ * In 1:, the input will be the GNUNET_CONFIGURATION_Handle to the TALER
+ * configuration and the output must be the struct TALER_Extension * on
+ * success, NULL otherwise.
+ *
+ * In 2:, no arguments are passed and NULL is expected to be returned.
*/
struct TALER_Extension
{
- /* simple linked list */
- struct TALER_Extension *next;
-
+ /**
+ * Type of the extension. Only one extension of a type can be loaded
+ * at any time.
+ */
enum TALER_Extension_Type type;
+
+ /**
+ * The name of the extension, must be unique among all loaded extensions. It
+ * is used in URLs for /extension/$NAME as well.
+ */
char *name;
+
+ /**
+ * Criticality of the extension. It has the same semantics as "critical" has
+ * for extensions in X.509:
+ * - if "true", the client must "understand" the extension before proceeding,
+ * - if "false", clients can safely skip extensions they do not understand.
+ * (see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)
+ */
bool critical;
+
+ /**
+ * Version of the extension must be provided in Taler's protocol verison ranges notation, see
+ * https://docs.taler.net/core/api-common.html#protocol-version-ranges
+ */
char *version;
+
+ /**
+ * If the extension is marked as enabled, it will be listed in the
+ * "extensions" field in the "/keys" response.
+ */
+ bool enabled;
+
+ /**
+ * Opaque (public) configuration object, set by the extension.
+ */
void *config;
- json_t *config_json;
- void (*disable)(struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*test_json_config)(
- const json_t *config);
+ /**
+ * @brief Handler to to disable the extension.
+ *
+ * @param ext The current extension object
+ */
+ void (*disable)(struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*load_json_config)(
+ /**
+ * @brief Handler to read an extension-specific configuration in JSON
+ * encoding and enable the extension. Must be implemented by the extension.
+ *
+ * @param ext The extension object. If NULL, the configuration will only be checked.
+ * @param config A JSON blob
+ * @return GNUNET_OK if the json was a valid configuration for the extension.
+ */
+ enum GNUNET_GenericReturnValue (*load_config)(
struct TALER_Extension *ext,
json_t *config);
- json_t *(*config_to_json)(
+ /**
+ * @brief Handler to return the manifest of the extension in JSON encoding.
+ *
+ * See
+ * https://docs.taler.net/design-documents/006-extensions.html#tsref-type-Extension
+ * for the definition.
+ *
+ * @param ext The extension object
+ * @return The JSON encoding of the extension, if enabled, NULL otherwise.
+ */
+ json_t *(*manifest)(
const struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*load_taler_config)(
- struct TALER_Extension *ext,
- const struct GNUNET_CONFIGURATION_Handle *cfg);
+ /* =========================
+ * Policy related handlers
+ * =========================
+ */
+
+ /**
+ * @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_json Details about the policy, provided by the client
+ * during a deposit request.
+ * @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 (*create_policy_details)(
+ const json_t *policy_json,
+ struct TALER_PolicyDetails *details,
+ const char **error_hint);
+
+ /**
+ * @brief Handler for POST-requests to the /extensions/$name endpoint. Can be NULL.
+ *
+ * @param[in] root The JSON body from the request
+ * @param[in] args Additional query parameters of the request.
+ * @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[],
+ struct TALER_PolicyDetails *details,
+ size_t details_len,
+ json_t **output);
+
+ /**
+ * @brief Handler for GET-requests to the /extensions/$name endpoint. Can be NULL.
+ *
+ * @param connection The current connection
+ * @param root The JSON body from the request
+ * @param args Additional query parameters of the request.
+ * @return MDH result
+ */
+ MHD_RESULT (*policy_get_handler)(
+ struct MHD_Connection *connection,
+ const char *const args[]);
+};
+
+
+/*
+ * @brief simply linked list of extensions
+ */
+
+struct TALER_Extensions
+{
+ struct TALER_Extensions *next;
+ const struct TALER_Extension *extension;
};
/**
@@ -73,70 +220,57 @@ struct TALER_Extension
*/
/*
- * @brief Sets the configuration of the extensions from the given TALER
- * configuration.
+ * @brief Loads the extensions as shared libraries, as specified in the given
+ * TALER configuration.
*
* @param cfg Handle to the TALER configuration
* @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
* or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_load_taler_config (
+TALER_extensions_init (
const struct GNUNET_CONFIGURATION_Handle *cfg);
/*
- * @brief Checks the given obj to be a valid extension object and fill the
- * fields accordingly.
+ * @brief Parses a given JSON object as an extension manifest.
*
- * @param[in] obj Object to verify is a valid extension
+ * @param[in] obj JSON object to parse as an extension manifest
* @param{out] critical will be set to 1 if the extension is critical according to obj
* @param[out] version will be set to the version of the extension according to obj
* @param[out] config will be set to the configuration of the extension according to obj
* @return OK on success, Error otherwise
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_is_json_config (
+TALER_extensions_parse_manifest (
json_t *obj,
int *critical,
const char **version,
json_t **config);
/*
- * @brief Sets the configuration of the extensions from a given JSON object.
+ * @brief Loads extensions according to the manifests.
*
- * The JSON object must be of type ExchangeKeysResponse as described in
- * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ * The JSON object must be of type ExtensionsManifestsResponse as described
+ * in https://docs.taler.net/design-documents/006-extensions.html#exchange
*
- * @param cfg JSON object containing the configuration for all extensions
+ * @param cfg JSON object containing the manifests for all extensions
* @return #GNUNET_OK on success, #GNUNET_SYSERR if unknown extensions were
* found or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_load_json_config (
- json_t *cfg);
+TALER_extensions_load_manifests (
+ json_t *manifests);
/*
* @brief Returns the head of the linked list of extensions.
*/
-const struct TALER_Extension *
+const struct TALER_Extensions *
TALER_extensions_get_head ();
-/*
- * @brief Adds an extension to the linked list of extensions.
- *
- * @param new_extension the new extension to be added
- * @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid
- * (missing fields), GNUNET_NO if there is already an extension with that name
- * or type.
- */
-enum GNUNET_GenericReturnValue
-TALER_extensions_add (
- struct TALER_Extension *new_extension);
-
/**
* @brief Finds and returns a supported extension by a given type.
*
- * @param type type of the extension to lookup
+ * @param type of the extension to lookup
* @return extension found, or NULL (should not happen!)
*/
const struct TALER_Extension *
@@ -154,8 +288,6 @@ const struct TALER_Extension *
TALER_extensions_get_by_name (
const char *name);
-#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
-
/**
* @brief Check if a given type of an extension is enabled
*
@@ -166,12 +298,21 @@ bool
TALER_extensions_is_enabled_type (
enum TALER_Extension_Type type);
+/**
+ * @brief Check if an extension is enabled
+ *
+ * @param extension The extension handler.
+ * @return true enabled, false if not enabled, will assert if type is not found.
+ */
+bool
+TALER_extensions_is_enabled (
+ const struct TALER_Extension *extension);
/*
* Verify the signature of a given JSON object for extensions with the master
* key of the exchange.
*
- * The JSON object must be of type ExchangeKeysResponse as described in
+ * The JSON object must be of type ExtensionsManifestsResponse as described in
* https://docs.taler.net/design-documents/006-extensions.html#exchange
*
* @param extensions JSON object with the extension configuration
@@ -181,14 +322,19 @@ TALER_extensions_is_enabled_type (
* and GNUNET_NO if the signature couldn't be verified.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_verify_json_config_signature (
- json_t *extensions,
+TALER_extensions_verify_manifests_signature (
+ json_t *manifests,
struct TALER_MasterSignatureP *extensions_sig,
struct TALER_MasterPublicKeyP *master_pub);
/*
* TALER Age Restriction Extension
+ *
+ * This extension is special insofar as it directly interacts with coins and
+ * denominations.
+ *
+ * At the same time, it doesn't implement and http- or deposit-handlers.
*/
#define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \
@@ -204,102 +350,39 @@ TALER_extensions_verify_json_config_signature (
| 1 << 21)
#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
-/**
- * @brief Registers the extension for age restriction to the list extensions
- */
-enum GNUNET_GenericReturnValue
-TALER_extension_age_restriction_register ();
-/**
- * @brief Parses a string as a list of age groups.
- *
- * The string must consist of a colon-separated list of increasing integers
- * between 0 and 31. Each entry represents the beginning of a new age group.
- * F.e. the string
- *
- * "8:10:12:14:16:18:21"
- *
- * represents the following list of eight age groups:
- *
- * | Group | Ages |
- * | -----:|:------------- |
- * | 0 | 0, 1, ..., 7 |
- * | 1 | 8, 9 |
- * | 2 | 10, 11 |
- * | 3 | 12, 13 |
- * | 4 | 14, 15 |
- * | 5 | 16, 17 |
- * | 6 | 18, 19, 20 |
- * | 7 | 21, ... |
- *
- * which is then encoded as a bit mask with the corresponding bits set:
- *
- * 31 24 16 8 0
- * | | | | |
- * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
- *
- * @param groups String representation of age groups
- * @param[out] mask Mask representation for age restriction.
- * @return Error, if age groups were invalid, OK otherwise.
+/*
+ * @brief Configuration for Age Restriction
*/
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (
- const char *groups,
- struct TALER_AgeMask *mask);
+struct TALER_AgeRestrictionConfig
+{
+ struct TALER_AgeMask mask;
+ uint8_t num_groups;
+};
-/**
- * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
- *
- * @param mask Age mask
- * @return String representation of the age mask, allocated by GNUNET_malloc.
- * Can be used as value in the TALER config.
- */
-char *
-TALER_age_mask_to_string (
- const struct TALER_AgeMask *mask);
/**
- * @brief Returns true when age restriction is configured and enabled.
+ * @brief Retrieve the age restriction configuration
+ *
+ * @return age restriction configuration if present, otherwise NULL.
*/
-bool
-TALER_extensions_age_restriction_is_enabled ();
+const struct TALER_AgeRestrictionConfig *
+TALER_extensions_get_age_restriction_config ();
/**
- * @brief Returns true when age restriction is configured (might not be
- * _enabled_, though).
+ * @brief Check if age restriction is enabled
+ *
+ * @return true, if age restriction is loaded, configured and enabled; otherwise false.
*/
bool
-TALER_extensions_age_restriction_is_configured ();
-
-/**
- * @brief Returns the currently set age mask. Note that even if age
- * restriction is not enabled, the age mask might be have a non-zero value.
- */
-struct TALER_AgeMask
-TALER_extensions_age_restriction_ageMask ();
-
+TALER_extensions_is_age_restriction_enabled ();
/**
- * @brief Returns the amount of age groups defined. 0 means no age restriction
- * enabled.
- */
-size_t
-TALER_extensions_age_restriction_num_groups ();
-
-/**
- * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
+ * @brief Return the age mask for age restriction
*
- * @param root is the json object
- * @param[out] mask on success, will contain the age mask
- * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
- */
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
- struct TALER_AgeMask *mask);
-
-
-/*
- * TODO: Add Peer2Peer Extension
+ * @return configured age mask, if age restriction is loaded, configured and enabled; otherwise zero mask.
*/
+struct TALER_AgeMask
+TALER_extensions_get_age_restriction_mask ();
#endif
diff --git a/src/include/taler_extensions_policy.h b/src/include/taler_extensions_policy.h
new file mode 100644
index 000000000..14a581f3c
--- /dev/null
+++ b/src/include/taler_extensions_policy.h
@@ -0,0 +1,198 @@
+/*
+ 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 */
+ 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
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 12526e954..d0527cc74 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -765,25 +765,25 @@ TALER_JSON_wire_to_payto (const json_t *wire_s);
/**
- * Hash @a extensions in deposits.
+ * Hash @a policy extensions in deposits.
*
- * @param extensions contract extensions to hash
- * @param[out] ech where to write the extension hash
+ * @param policy contract policy extension to hash
+ * @param[out] ech where to write the policy hash
*/
void
-TALER_deposit_extension_hash (const json_t *extensions,
- struct TALER_ExtensionContractHashP *ech);
+TALER_deposit_policy_hash (const json_t *extensions,
+ struct TALER_ExtensionPolicyHashP *ech);
/**
- * Hash the @a config of an extension, given as JSON
+ * Hash the @a manifests of extensions, given as JSON
*
- * @param config configuration of the extension
- * @param[out] eh where to write the extension hash
+ * @param manifests Manifests of the extensions
+ * @param[out] eh where to write the hash
* @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
enum GNUNET_GenericReturnValue
-TALER_JSON_extensions_config_hash (const json_t *config,
- struct TALER_ExtensionConfigHashP *eh);
+TALER_JSON_extensions_manifests_hash (const json_t *manifests,
+ struct TALER_ExtensionManifestsHashP *eh);
/**
* Canonicalize a JSON input to a string according to RFC 8785.
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 079f72ed6..1580f3dca 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -492,5 +492,63 @@ char *strchrnul (const char *s, int c);
#endif
+/**
+ * @brief Parses a string as a list of age groups.
+ *
+ * The string must consist of a colon-separated list of increasing integers
+ * between 0 and 31. Each entry represents the beginning of a new age group.
+ * F.e. the string
+ *
+ * "8:10:12:14:16:18:21"
+ *
+ * represents the following list of eight age groups:
+ *
+ * | Group | Ages |
+ * | -----:|:------------- |
+ * | 0 | 0, 1, ..., 7 |
+ * | 1 | 8, 9 |
+ * | 2 | 10, 11 |
+ * | 3 | 12, 13 |
+ * | 4 | 14, 15 |
+ * | 5 | 16, 17 |
+ * | 6 | 18, 19, 20 |
+ * | 7 | 21, ... |
+ *
+ * which is then encoded as a bit mask with the corresponding bits set:
+ *
+ * 31 24 16 8 0
+ * | | | | |
+ * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
+ *
+ * @param groups String representation of age groups
+ * @param[out] mask Mask representation for age restriction.
+ * @return Error, if age groups were invalid, OK otherwise.
+ */
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+ const char *groups,
+ struct TALER_AgeMask *mask);
+
+/**
+ * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
+ *
+ * @param mask Age mask
+ * @return String representation of the age mask, allocated by GNUNET_malloc.
+ * Can be used as value in the TALER config.
+ */
+char *
+TALER_age_mask_to_string (
+ const struct TALER_AgeMask *mask);
+
+/**
+ * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
+ *
+ * @param root is the json object
+ * @param[out] mask on success, will contain the age mask
+ * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
+ */
+enum GNUNET_GenericReturnValue
+TALER_JSON_parse_age_groups (const json_t *root,
+ struct TALER_AgeMask *mask);
#endif
diff --git a/src/json/json.c b/src/json/json.c
index d4ac37489..7d7e4ecba 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -1008,12 +1008,12 @@ TALER_JSON_get_error_code2 (const void *data,
void
-TALER_deposit_extension_hash (const json_t *extensions,
- struct TALER_ExtensionContractHashP *ech)
+TALER_deposit_policy_hash (const json_t *policy,
+ struct TALER_ExtensionPolicyHashP *ech)
{
GNUNET_assert (GNUNET_OK ==
- dump_and_hash (extensions,
- "taler-contract-extensions",
+ dump_and_hash (policy,
+ "taler-extensions-policy",
&ech->hash));
}
@@ -1037,11 +1037,11 @@ TALER_JSON_canonicalize (const json_t *input)
enum GNUNET_GenericReturnValue
-TALER_JSON_extensions_config_hash (const json_t *config,
- struct TALER_ExtensionConfigHashP *ech)
+TALER_JSON_extensions_manifests_hash (const json_t *manifests,
+ struct TALER_ExtensionManifestsHashP *ech)
{
- return dump_and_hash (config,
- "taler-extension-configuration",
+ return dump_and_hash (manifests,
+ "taler-extensions-manifests",
&ech->hash);
}
diff --git a/src/lib/auditor_api_deposit_confirmation.c b/src/lib/auditor_api_deposit_confirmation.c
index c4542d0eb..82537e25a 100644
--- a/src/lib/auditor_api_deposit_confirmation.c
+++ b/src/lib/auditor_api_deposit_confirmation.c
@@ -153,7 +153,7 @@ handle_deposit_confirmation_finished (void *cls,
* Verify signature information about the deposit-confirmation.
*
* @param h_wire hash of merchant wire details
- * @param h_extensions hash over the extensions, if any
+ * @param h_policy hash over the policy extension, if any
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
* @param exchange_timestamp timestamp when the deposit was received by the wallet
* @param wire_deadline by what time must the amount be wired to the merchant
@@ -172,7 +172,7 @@ handle_deposit_confirmation_finished (void *cls,
*/
static enum GNUNET_GenericReturnValue
verify_signatures (const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_PrivateContractHashP *h_contract_terms,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
@@ -192,7 +192,7 @@ verify_signatures (const struct TALER_MerchantWireHashP *h_wire,
TALER_exchange_online_deposit_confirmation_verify (
h_contract_terms,
h_wire,
- h_extensions,
+ h_policy,
exchange_timestamp,
wire_deadline,
refund_deadline,
@@ -239,7 +239,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle *
TALER_AUDITOR_deposit_confirmation (
struct TALER_AUDITOR_Handle *auditor,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_PrivateContractHashP *h_contract_terms,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
@@ -266,7 +266,7 @@ TALER_AUDITOR_deposit_confirmation (
TALER_AUDITOR_handle_is_ready_ (auditor));
if (GNUNET_OK !=
verify_signatures (h_wire,
- h_extensions,
+ h_policy,
h_contract_terms,
exchange_timestamp,
wire_deadline,
@@ -290,8 +290,8 @@ TALER_AUDITOR_deposit_confirmation (
= GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("h_wire",
h_wire),
- GNUNET_JSON_pack_data_auto ("h_extensions",
- h_extensions),
+ GNUNET_JSON_pack_data_auto ("h_policy",
+ h_policy),
GNUNET_JSON_pack_data_auto ("h_contract_terms",
h_contract_terms),
GNUNET_JSON_pack_timestamp ("exchange_timestamp",
diff --git a/src/lib/exchange_api_batch_deposit.c b/src/lib/exchange_api_batch_deposit.c
index 39c1c9b4f..ca5c3c618 100644
--- a/src/lib/exchange_api_batch_deposit.c
+++ b/src/lib/exchange_api_batch_deposit.c
@@ -99,7 +99,7 @@ struct TALER_EXCHANGE_BatchDepositHandle
/**
* Hash over the extensions, or all zero.
*/
- struct TALER_ExtensionContractHashP h_extensions;
+ struct TALER_ExtensionPolicyHashP h_policy;
/**
* Time when this confirmation was generated / when the exchange received
@@ -185,7 +185,7 @@ auditor_cb (void *cls,
aie->dch = TALER_AUDITOR_deposit_confirmation (
ah,
&dh->h_wire,
- &dh->h_extensions,
+ &dh->h_policy,
&dh->dcd.h_contract_terms,
dh->exchange_timestamp,
dh->dcd.wire_deadline,
@@ -317,7 +317,7 @@ handle_deposit_finished (void *cls,
TALER_exchange_online_deposit_confirmation_verify (
&dh->dcd.h_contract_terms,
&dh->h_wire,
- &dh->h_extensions,
+ &dh->h_policy,
dh->exchange_timestamp,
dh->dcd.wire_deadline,
dh->dcd.refund_deadline,
@@ -492,9 +492,9 @@ TALER_EXCHANGE_batch_deposit (
* sizeof (*cdds));
dh->num_cdds = num_cdds;
dh->dcd = *dcd;
- if (NULL != dcd->extension_details)
- TALER_deposit_extension_hash (dcd->extension_details,
- &dh->h_extensions);
+ if (NULL != dcd->policy_details)
+ TALER_deposit_policy_hash (dcd->policy_details,
+ &dh->h_policy);
TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
&dcd->wire_salt,
&dh->h_wire);
@@ -533,7 +533,7 @@ TALER_EXCHANGE_batch_deposit (
if (GNUNET_OK !=
TALER_EXCHANGE_verify_deposit_signature_ (dcd,
- &dh->h_extensions,
+ &dh->h_policy,
&dh->h_wire,
cdd,
dki))
@@ -586,8 +586,8 @@ TALER_EXCHANGE_batch_deposit (
GNUNET_JSON_pack_array_steal ("coins",
deposits),
GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_steal ("extension_details",
- NULL)), /* FIXME #7270-Oec */
+ GNUNET_JSON_pack_object_steal ("policy_details",
+ dcd->policy_details)),
GNUNET_JSON_pack_timestamp ("timestamp",
dcd->timestamp),
GNUNET_JSON_pack_data_auto ("merchant_pub",
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index 3041e6e0e..3807b997f 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -844,7 +844,8 @@ help_deposit (struct CoinHistoryParseContext *pc,
{
struct TALER_MerchantWireHashP h_wire;
struct TALER_PrivateContractHashP h_contract_terms;
- // struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
+ struct TALER_ExtensionPolicyHashP h_policy;
+ bool no_h_policy;
struct GNUNET_TIME_Timestamp wallet_timestamp;
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_TIME_Timestamp refund_deadline = {0};
@@ -863,6 +864,10 @@ help_deposit (struct CoinHistoryParseContext *pc,
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&hac),
&no_hac),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("h_policy",
+ &h_policy),
+ &no_h_policy),
GNUNET_JSON_spec_timestamp ("timestamp",
&wallet_timestamp),
GNUNET_JSON_spec_mark_optional (
@@ -891,7 +896,7 @@ help_deposit (struct CoinHistoryParseContext *pc,
&h_wire,
&h_contract_terms,
no_hac ? NULL : &hac,
- NULL /* h_extensions! */,
+ no_h_policy ? NULL : &h_policy,
&pc->dk->h_key,
wallet_timestamp,
&merchant_pub,
@@ -2143,7 +2148,7 @@ TALER_EXCHANGE_get_min_denomination_ (
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_deposit_signature_ (
const struct TALER_EXCHANGE_DepositContractDetail *dcd,
- const struct TALER_ExtensionContractHashP *ech,
+ const struct TALER_ExtensionPolicyHashP *ech,
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
const struct TALER_EXCHANGE_DenomPublicKey *dki)
diff --git a/src/lib/exchange_api_common.h b/src/lib/exchange_api_common.h
index a75ed3ed2..80c36daf1 100644
--- a/src/lib/exchange_api_common.h
+++ b/src/lib/exchange_api_common.h
@@ -203,7 +203,7 @@ TALER_EXCHANGE_get_min_denomination_ (
* Verify signature information about the deposit.
*
* @param dcd contract details
- * @param ech hashed contract (passed to avoid recomputation)
+ * @param ech hashed policy (passed to avoid recomputation)
* @param h_wire hashed wire details (passed to avoid recomputation)
* @param cdd coin-specific details
* @param dki denomination of the coin
@@ -212,7 +212,7 @@ TALER_EXCHANGE_get_min_denomination_ (
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_deposit_signature_ (
const struct TALER_EXCHANGE_DepositContractDetail *dcd,
- const struct TALER_ExtensionContractHashP *ech,
+ const struct TALER_ExtensionPolicyHashP *ech,
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
const struct TALER_EXCHANGE_DenomPublicKey *dki);
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 3ba986b2d..8f179b72c 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -97,9 +97,9 @@ struct TALER_EXCHANGE_DepositHandle
struct TALER_MerchantWireHashP h_wire;
/**
- * Hash over the extensions, or all zero.
+ * Hash over the policy extension, or all zero.
*/
- struct TALER_ExtensionContractHashP h_extensions;
+ struct TALER_ExtensionPolicyHashP h_policy;
/**
* Time when this confirmation was generated / when the exchange received
@@ -177,7 +177,7 @@ auditor_cb (void *cls,
aie->dch = TALER_AUDITOR_deposit_confirmation (
ah,
&dh->h_wire,
- &dh->h_extensions,
+ &dh->h_policy,
&dh->dcd.h_contract_terms,
dh->exchange_timestamp,
dh->dcd.wire_deadline,
@@ -277,7 +277,7 @@ handle_deposit_finished (void *cls,
TALER_exchange_online_deposit_confirmation_verify (
&dh->dcd.h_contract_terms,
&dh->h_wire,
- &dh->h_extensions,
+ &dh->h_policy,
dh->exchange_timestamp,
dh->dcd.wire_deadline,
dh->dcd.refund_deadline,
@@ -446,15 +446,15 @@ TALER_EXCHANGE_deposit (
dh->cb_cls = cb_cls;
dh->cdd = *cdd;
dh->dcd = *dcd;
- if (NULL != dcd->extension_details)
- TALER_deposit_extension_hash (dcd->extension_details,
- &dh->h_extensions);
+ if (NULL != dcd->policy_details)
+ TALER_deposit_policy_hash (dcd->policy_details,
+ &dh->h_policy);
TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
&dcd->wire_salt,
&dh->h_wire);
if (GNUNET_OK !=
TALER_EXCHANGE_verify_deposit_signature_ (dcd,
- &dh->h_extensions,
+ &dh->h_policy,
&dh->h_wire,
cdd,
dki))
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index d6e12cc17..14ba73174 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -898,17 +898,20 @@ decode_keys_json (const json_t *resp_obj,
/* TODO: maybe lift all this into a FP in TALER_Extension ? */
{
struct TALER_MasterSignatureP extensions_sig = {0};
- json_t *extensions = NULL;
+ json_t *manifests = NULL;
+ bool no_extensions = false;
+ bool no_signature = false;
+
struct GNUNET_JSON_Specification ext_spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("extensions",
- &extensions),
- NULL),
+ &manifests),
+ &no_extensions),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto (
"extensions_sig",
&extensions_sig),
- NULL),
+ &no_signature),
GNUNET_JSON_spec_end ()
};
@@ -918,22 +921,27 @@ decode_keys_json (const json_t *resp_obj,
ext_spec,
NULL, NULL));
- if (NULL != extensions)
+
+ if (! no_extensions && no_signature)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "found extensions without signature\n");
+
+ if (! no_extensions && ! no_signature)
{
/* 2. We have an extensions object. Verify its signature. */
EXITIF (GNUNET_OK !=
- TALER_extensions_verify_json_config_signature (
- extensions,
+ TALER_extensions_verify_manifests_signature (
+ manifests,
&extensions_sig,
&key_data->master_pub));
/* 3. Parse and set the the configuration of the extensions accordingly */
EXITIF (GNUNET_OK !=
- TALER_extensions_load_json_config (extensions));
+ TALER_extensions_load_manifests (manifests));
}
/* 4. assuming we might have now a new value for age_mask, set it in key_data */
- key_data->age_mask = TALER_extensions_age_restriction_ageMask ();
+ key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
}
/**
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index a937c18af..855b4fcc7 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -236,7 +236,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
struct TALER_PrivateContractHashP h_contract_terms;
struct TALER_AgeCommitmentHash h_age_commitment;
bool no_hac;
- // struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
+ struct TALER_ExtensionPolicyHashP h_policy;
+ bool no_h_policy;
struct GNUNET_TIME_Timestamp wallet_timestamp;
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_TIME_Timestamp refund_deadline;
@@ -252,6 +253,10 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&h_age_commitment),
&no_hac),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("h_policy",
+ &h_policy),
+ &no_h_policy),
GNUNET_JSON_spec_timestamp ("timestamp",
&wallet_timestamp),
GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -277,10 +282,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
&deposit_fee,
&h_wire,
&h_contract_terms,
- no_hac
- ? NULL
- : &h_age_commitment,
- NULL /* FIXME #7270-OEC: h_extensions! */,
+ no_hac ? NULL : &h_age_commitment,
+ no_h_policy ? NULL: &h_policy,
&h_denom_pub,
wallet_timestamp,
&merchant_pub,
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index dd39ee940..ec9ccb741 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -1312,9 +1312,6 @@ main (int argc,
"INFO",
NULL);
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
-
cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
GNUNET_assert (NULL != cipher);
uses_cs = (0 == strcmp (cipher, "cs"));
diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c
index 27d47bfd4..f159b2f1b 100644
--- a/src/testing/test_exchange_p2p.c
+++ b/src/testing/test_exchange_p2p.c
@@ -522,9 +522,6 @@ main (int argc,
"INFO",
NULL);
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
-
cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
GNUNET_assert (NULL != cipher);
uses_cs = (0 == strcmp (cipher, "cs"));
diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
index d99b12937..293ecba27 100644
--- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
+++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
@@ -199,7 +199,7 @@ deposit_confirmation_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
- static struct TALER_ExtensionContractHashP no_h_extensions;
+ static struct TALER_ExtensionPolicyHashP no_h_policy;
struct DepositConfirmationState *dcs = cls;
const struct TALER_TESTING_Command *deposit_cmd;
struct TALER_MerchantWireHashP h_wire;
@@ -310,7 +310,7 @@ deposit_confirmation_run (void *cls,
}
dcs->dc = TALER_AUDITOR_deposit_confirmation (dcs->auditor,
&h_wire,
- &no_h_extensions,
+ &no_h_policy,
&h_contract_terms,
*exchange_timestamp,
*wire_deadline,
diff --git a/src/testing/testing_api_cmd_batch_deposit.c b/src/testing/testing_api_cmd_batch_deposit.c
index 967a5ac33..54a20cdb2 100644
--- a/src/testing/testing_api_cmd_batch_deposit.c
+++ b/src/testing/testing_api_cmd_batch_deposit.c
@@ -381,7 +381,7 @@ batch_deposit_run (void *cls,
.merchant_payto_uri = payto_uri,
.wire_salt = wire_salt,
.h_contract_terms = h_contract_terms,
- .extension_details = NULL /* FIXME #7270-OEC */,
+ .policy_details = NULL /* FIXME #7270-OEC */,
.timestamp = ds->wallet_timestamp,
.merchant_pub = merchant_pub,
.refund_deadline = ds->refund_deadline
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c
index b68ca99f7..a5229ae9c 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash);
- mask = TALER_extensions_age_restriction_ageMask ();
+ mask = TALER_extensions_get_age_restriction_mask ();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed,
sizeof(seed));
diff --git a/src/testing/testing_api_cmd_deposit.c b/src/testing/testing_api_cmd_deposit.c
index f66947678..16ac139f4 100644
--- a/src/testing/testing_api_cmd_deposit.c
+++ b/src/testing/testing_api_cmd_deposit.c
@@ -468,7 +468,7 @@ deposit_run (void *cls,
.merchant_payto_uri = payto_uri,
.wire_salt = wire_salt,
.h_contract_terms = h_contract_terms,
- .extension_details = NULL /* FIXME #7270-OEC */,
+ .policy_details = NULL /* FIXME #7270-OEC */,
.timestamp = ds->wallet_timestamp,
.merchant_pub = merchant_pub,
.refund_deadline = ds->refund_deadline
diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c
index ce2c49ccc..7a81d1c33 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash);
- mask = TALER_extensions_age_restriction_ageMask ();
+ mask = TALER_extensions_get_age_restriction_mask ();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed,
sizeof(seed));
diff --git a/src/testing/testing_api_helpers_exchange.c b/src/testing/testing_api_helpers_exchange.c
index d813021bd..7fe0038ad 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -314,8 +314,13 @@ sign_keys_for_exchange (void *cls,
char *exchange_master_pub;
int ret;
- /* Load the age restriction mask from the configuration */
- TALER_extensions_load_taler_config (cfg);
+ /* Load the extensions */
+ if (GNUNET_OK != TALER_extensions_init (cfg))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "couldn't load extensions");
+ return GNUNET_SYSERR;
+ }
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 94edac021..acafdae57 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \
libtalerutil_la_LIBADD = \
-lgnunetutil \
+ -lgnunetjson \
-lsodium \
-ljansson \
$(LIBGCRYPT_LIBS) \
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index b87c8543e..f4ac9abea 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -21,6 +21,7 @@
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
+#include <gnunet/gnunet_json_lib.h>
#include <gcrypt.h>
void
@@ -436,3 +437,113 @@ TALER_age_commitment_proof_free (
cp->commitment.keys = NULL;
}
}
+
+
+enum GNUNET_GenericReturnValue
+TALER_JSON_parse_age_groups (const json_t *root,
+ struct TALER_AgeMask *mask)
+{
+ enum GNUNET_GenericReturnValue ret;
+ const char *str;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("age_groups",
+ &str),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ret = GNUNET_JSON_parse (root,
+ spec,
+ NULL,
+ NULL);
+ if (GNUNET_OK == ret)
+ TALER_parse_age_group_string (str, mask);
+
+ GNUNET_JSON_parse_free (spec);
+
+ return ret;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+ const char *groups,
+ struct TALER_AgeMask *mask)
+{
+
+ const char *pos = groups;
+ unsigned int prev = 0;
+ unsigned int val = 0;
+ char c;
+
+ while (*pos)
+ {
+ c = *pos++;
+ if (':' == c)
+ {
+ if (prev >= val)
+ return GNUNET_SYSERR;
+
+ mask->bits |= 1 << val;
+ prev = val;
+ val = 0;
+ continue;
+ }
+
+ if ('0'>c || '9'<c)
+ return GNUNET_SYSERR;
+
+ val = 10 * val + c - '0';
+
+ if (0>=val || 32<=val)
+ return GNUNET_SYSERR;
+ }
+
+ if (32<=val || prev>=val)
+ return GNUNET_SYSERR;
+
+ mask->bits |= (1 << val);
+ mask->bits |= 1; // mark zeroth group, too
+
+ return GNUNET_OK;
+}
+
+
+char *
+TALER_age_mask_to_string (
+ const struct TALER_AgeMask *mask)
+{
+ uint32_t bits = mask->bits;
+ unsigned int n = 0;
+ char *buf = GNUNET_malloc (32 * 3); // max characters possible
+ char *pos = buf;
+
+ if (NULL == buf)
+ {
+ return buf;
+ }
+
+ while (bits != 0)
+ {
+ bits >>= 1;
+ n++;
+ if (0 == (bits & 1))
+ {
+ continue;
+ }
+
+ if (n > 9)
+ {
+ *(pos++) = '0' + n / 10;
+ }
+ *(pos++) = '0' + n % 10;
+
+ if (0 != (bits >> 1))
+ {
+ *(pos++) = ':';
+ }
+ }
+ return buf;
+}
+
+
+/* end util/age_restriction.c */
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index 3169cb84c..1762d5a50 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -48,10 +48,10 @@ struct TALER_DepositConfirmationPS
struct TALER_MerchantWireHashP h_wire GNUNET_PACKED;
/**
- * Hash over the extension options of the deposit, 0 if there
- * were not extension options.
+ * Hash over the optional policy extension of the deposit, 0 if there
+ * was no policy.
*/
- struct TALER_ExtensionContractHashP h_extensions GNUNET_PACKED;
+ struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED;
/**
* Time when this confirmation was generated / when the exchange received
@@ -101,7 +101,7 @@ TALER_exchange_online_deposit_confirmation_sign (
TALER_ExchangeSignCallback scb,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -123,8 +123,8 @@ TALER_exchange_online_deposit_confirmation_sign (
.merchant_pub = *merchant_pub
};
- if (NULL != h_extensions)
- dcs.h_extensions = *h_extensions;
+ if (NULL != h_policy)
+ dcs.h_policy = *h_policy;
TALER_amount_hton (&dcs.amount_without_fee,
amount_without_fee);
return scb (&dcs.purpose,
@@ -137,7 +137,7 @@ enum GNUNET_GenericReturnValue
TALER_exchange_online_deposit_confirmation_verify (
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -159,8 +159,8 @@ TALER_exchange_online_deposit_confirmation_verify (
.merchant_pub = *merchant_pub
};
- if (NULL != h_extensions)
- dcs.h_extensions = *h_extensions;
+ if (NULL != h_policy)
+ dcs.h_policy = *h_policy;
TALER_amount_hton (&dcs.amount_without_fee,
amount_without_fee);
if (GNUNET_OK !=
diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c
index b316c8ba7..d0b644e7f 100644
--- a/src/util/offline_signatures.c
+++ b/src/util/offline_signatures.c
@@ -926,10 +926,10 @@ TALER_exchange_offline_global_fee_verify (
GNUNET_NETWORK_STRUCT_BEGIN
/**
- * @brief Signature made by the exchange offline key over the
- * configuration of an extension.
+ * @brief Signature made by the exchange offline key over the manifest of
+ * an extension.
*/
-struct TALER_MasterExtensionConfigurationPS
+struct TALER_MasterExtensionManifestPS
{
/**
* Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed
@@ -938,24 +938,24 @@ struct TALER_MasterExtensionConfigurationPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
- * Hash of the JSON object that represents the configuration of an extension.
+ * Hash of the JSON object that represents the manifests of extensions.
*/
- struct TALER_ExtensionConfigHashP h_config GNUNET_PACKED;
+ struct TALER_ExtensionManifestsHashP h_manifest GNUNET_PACKED;
};
GNUNET_NETWORK_STRUCT_END
void
-TALER_exchange_offline_extension_config_hash_sign (
- const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_sign (
+ const struct TALER_ExtensionManifestsHashP *h_manifest,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig)
{
- struct TALER_MasterExtensionConfigurationPS ec = {
+ struct TALER_MasterExtensionManifestPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ec)),
- .h_config = *h_config
+ .h_manifest = *h_manifest
};
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
&ec,
@@ -964,16 +964,16 @@ TALER_exchange_offline_extension_config_hash_sign (
enum GNUNET_GenericReturnValue
-TALER_exchange_offline_extension_config_hash_verify (
- const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_verify (
+ const struct TALER_ExtensionManifestsHashP *h_manifest,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
)
{
- struct TALER_MasterExtensionConfigurationPS ec = {
+ struct TALER_MasterExtensionManifestPS ec = {
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
.purpose.size = htonl (sizeof(ec)),
- .h_config = *h_config
+ .h_manifest = *h_manifest
};
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index 6c8124d03..5efcc5d64 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -49,9 +49,9 @@ struct TALER_DepositRequestPS
struct TALER_AgeCommitmentHash h_age_commitment GNUNET_PACKED;
/**
- * Hash over extension attributes shared with the exchange.
+ * Hash over optional policy extension attributes shared with the exchange.
*/
- struct TALER_ExtensionContractHashP h_extensions GNUNET_PACKED;
+ struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED;
/**
* Hash over the wiring information of the merchant.
@@ -120,7 +120,7 @@ TALER_wallet_deposit_sign (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_DenominationHashP *h_denom_pub,
const struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -141,8 +141,8 @@ TALER_wallet_deposit_sign (
if (NULL != h_age_commitment)
dr.h_age_commitment = *h_age_commitment;
- if (NULL != h_extensions)
- dr.h_extensions = *h_extensions;
+ if (NULL != h_policy)
+ dr.h_policy = *h_policy;
TALER_amount_hton (&dr.amount_with_fee,
amount);
TALER_amount_hton (&dr.deposit_fee,
@@ -160,7 +160,7 @@ TALER_wallet_deposit_verify (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_DenominationHashP *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -178,13 +178,13 @@ TALER_wallet_deposit_verify (
.refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
.merchant = *merchant_pub,
.h_age_commitment = {{{0}}},
- .h_extensions = {{{0}}}
+ .h_policy = {{{0}}}
};
if (NULL != h_age_commitment)
dr.h_age_commitment = *h_age_commitment;
- if (NULL != h_extensions)
- dr.h_extensions = *h_extensions;
+ if (NULL != h_policy)
+ dr.h_policy = *h_policy;
TALER_amount_hton (&dr.amount_with_fee,
amount);
TALER_amount_hton (&dr.deposit_fee,