policy extensions and age restriction refactoring

- refactoring of extension-plugin-mechanism
- refactoring of age restriction extension
- added policy extensions plugin plumbing
- added DB schema and api
  - policy_details
  - policy_fulfillments
This commit is contained in:
Özgür Kesim 2022-11-04 12:18:16 +01:00
parent c89bfa9026
commit 752f102738
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
67 changed files with 2673 additions and 1193 deletions

View File

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

View File

@ -32,5 +32,6 @@ SUBDIRS = \
auditor \
lib \
exchange-tools \
extensions/age_restriction \
testing \
benchmark

View File

@ -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",

View File

@ -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},

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

@ -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"

View File

@ -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"

View File

@ -137,6 +137,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().
*/
@ -163,11 +169,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.
*/
@ -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,7 +4298,7 @@ do_extensions_sign (char *const *args)
return;
}
TALER_exchange_offline_extension_config_hash_sign (&h_config,
TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests,
&master_priv,
&sig);
obj = GNUNET_JSON_PACK (
@ -4297,9 +4307,10 @@ do_extensions_sign (char *const *args)
GNUNET_JSON_pack_data_auto (
"extensions_sig",
&sig));
output_operation (OP_EXTENSIONS,
obj);
next (args + 1);
next (args);
}
@ -4500,6 +4511,24 @@ run (void *cls,
(void) cls;
(void) cfgfile;
kcfg = cfg;
/* load extensions */
GNUNET_assert (GNUNET_OK ==
TALER_extensions_init (kcfg));
/* setup age restriction, if applicable */
{
const struct TALER_AgeRestrictionConfig *arc;
if (NULL !=
(arc = TALER_extensions_get_age_restriction_config ()))
{
ar_config = *arc;
ar_enabled = true;
}
}
if (GNUNET_OK !=
TALER_config_get_currency (kcfg,
&currency))
@ -4508,18 +4537,6 @@ run (void *cls,
return;
}
/* 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))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while loading taler config for extensions\n");
return;
}
age_mask = TALER_extensions_age_restriction_ageMask ();
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);

View File

@ -101,6 +101,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.
*/
@ -142,11 +150,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.
*/
@ -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()

View File

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

View File

@ -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,12 +481,13 @@ 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,
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,
dc->has_policy ? &dc->h_policy : NULL,
&deposit->coin.denom_pub_hash,
dc->timestamp,
&dc->merchant_pub,
@ -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),
do {
res = parse_coin (connection,
json_array_get (coins, i),
&dc,
&dc.deposits[i])))
&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);

View File

@ -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,11 +187,32 @@ deposit_transaction (void *cls,
mhd_ret);
if (qs < 0)
return qs;
qs = TEH_plugin->do_deposit (TEH_plugin->cls,
/* 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,
false, /* FIXME-OEC: extension blocked #7270 */
(dc->has_policy)
? &dc->policy_details_serial_id
: NULL,
&dc->exchange_timestamp,
&balance_ok,
&in_conflict);
@ -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,

View File

@ -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,
qs = TEH_plugin->get_extension_manifest (TEH_plugin->cls,
extension->name,
&config_str);
&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 */

View File

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

View File

@ -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,9 +824,6 @@ load_age_mask (const char*section_name)
"AGE_RESTRICTED")))
return null_mask;
{
enum GNUNET_GenericReturnValue ret;
if (GNUNET_SYSERR ==
(ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
section_name,
@ -840,8 +835,18 @@ load_age_mask (const char*section_name)
"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,6 +1941,9 @@ create_krd (struct TEH_KeyStateHandle *ksh,
extensions);
GNUNET_assert (0 == r);
/* 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));
@ -1955,6 +1951,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
r = json_object_update (keys, sig);
GNUNET_assert (0 == r);
}
}
else
{
json_decref (extensions);

View File

@ -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);

View File

@ -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! */
};
/**

View File

@ -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);

View File

@ -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,

View File

@ -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
;

View File

@ -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 ----------------------------------------
-- ------------------------------ policy_fulfillments -------------------------------------
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 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';
CREATE TABLE IF NOT EXISTS extension_details_default
PARTITION OF extension_details
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
-- ------------------------------ 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

View File

@ -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,
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;

View File

@ -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,
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",

View File

@ -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:

View File

@ -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,
postgres_set_extension_manifest (void *cls,
const char *extension_name,
const char *config)
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,
postgres_get_extension_manifest (void *cls,
const char *extension_name,
char **config)
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

View File

@ -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;

View File

@ -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,
plugin->get_extension_manifest (plugin->cls,
"fnord",
&config));
&manifest));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->set_extension_config (plugin->cls,
plugin->set_extension_manifest (plugin->cls,
"fnord",
"bar"));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_extension_config (plugin->cls,
plugin->get_extension_manifest (plugin->cls,
"fnord",
&config));
&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,
plugin->set_extension_manifest (plugin->cls,
"fnord",
"buzz"));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_extension_config (plugin->cls,
plugin->get_extension_manifest (plugin->cls,
"fnord",
&config));
&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,
plugin->set_extension_manifest (plugin->cls,
"fnord",
NULL));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_extension_config (plugin->cls,
plugin->get_extension_manifest (plugin->cls,
"fnord",
&config));
&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 ==

View File

@ -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)

View File

@ -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)

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unsupported extension `%s` (section [%s]).\n", name,
section);
col->error = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
extension->load_taler_config (
(struct TALER_Extension *) extension,
col->cfg))
/* 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,
"Couldn't parse configuration for extension `%s` (section [%s]).\n",
"Couldn't load extension library to `%s` (section [%s]).\n",
name,
section);
col->error = GNUNET_SYSERR;
return;
}
if (GNUNET_OK != add_extension (extension))
{
/* TODO: Ignoring return values here */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"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,21 +295,22 @@ 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 =
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 ()
{
const struct TALER_Extension *age =
TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
/*
* 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)
{
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 ());
}

View File

@ -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,

View File

@ -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.

View File

@ -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 merchants 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 coins 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 merchants 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 merchants 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 merchants 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
);

View File

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

View File

@ -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
{
@ -1511,12 +1532,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.
@ -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);
@ -3580,6 +3650,19 @@ struct TALER_EXCHANGEDB_Plugin
bool *balance_ok);
/**
* Add a proof of fulfillment of an policy
*
* @param cls the plugin-specific state
* @param[in,out] fulfillment The proof of fulfillment and serial_ids of the policy_details along with their new state and potential new amounts.
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
(*add_policy_fulfillment_proof)(
void *cls,
struct TALER_PolicyFulfillmentTransactionData *fulfillment);
/**
* Check if the given @a nonce was properly locked to the given @a old_coin_pub. If so, check if we already
* created CS signatures for the given @a nonce and @a new_denom_pub_hashes,
@ -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,
(*set_extension_manifest)(void *cls,
const char *extension_name,
const char *config);
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,
(*get_extension_manifest)(void *cls,
const char *extension_name,
char **config);
char **manifest);
/**

View File

@ -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;
char *name;
bool critical;
char *version;
void *config;
json_t *config_json;
/**
* 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;
/**
* @brief Handler to to disable the extension.
*
* @param ext The current extension object
*/
void (*disable)(struct TALER_Extension *ext);
enum GNUNET_GenericReturnValue (*test_json_config)(
const json_t *config);
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.
*/
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 Returns true when age restriction is configured and enabled.
*/
bool
TALER_extensions_age_restriction_is_enabled ();
/**
* @brief Returns true when age restriction is configured (might not be
* _enabled_, though).
*/
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 ();
/**
* @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" }.
*
* @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
* @brief Configuration for Age Restriction
*/
struct TALER_AgeRestrictionConfig
{
struct TALER_AgeMask mask;
uint8_t num_groups;
};
/**
* @brief Retrieve the age restriction configuration
*
* @return age restriction configuration if present, otherwise NULL.
*/
const struct TALER_AgeRestrictionConfig *
TALER_extensions_get_age_restriction_config ();
/**
* @brief Check if age restriction is enabled
*
* @return true, if age restriction is loaded, configured and enabled; otherwise false.
*/
bool
TALER_extensions_is_age_restriction_enabled ();
/**
* @brief Return the age mask for age restriction
*
* @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

View File

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

View File

@ -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.

View File

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

View File

@ -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);
}

View File

@ -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",

View File

@ -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",

View File

@ -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)

View File

@ -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);

View File

@ -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))

View File

@ -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 ();
}
/**

View File

@ -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,

View File

@ -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"));

View File

@ -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"));

View File

@ -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,

View File

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

View File

@ -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));

View File

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

View File

@ -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));

View File

@ -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,

View File

@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \
libtalerutil_la_LIBADD = \
-lgnunetutil \
-lgnunetjson \
-lsodium \
-ljansson \
$(LIBGCRYPT_LIBS) \

View File

@ -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 */

View File

@ -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 !=

View File

@ -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,

View File

@ -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,