Compare commits
32 Commits
master
...
auction_br
Author | SHA1 | Date | |
---|---|---|---|
949a91c38e | |||
63b19fad0c | |||
29e5cd0ef5 | |||
15e8697ece | |||
054e157af8 | |||
956e3c3065 | |||
18d8dcd1f9 | |||
4154a0c466 | |||
4ba07b54e4 | |||
d3c509fcd9 | |||
52106eea42 | |||
dd60d9b984 | |||
b447506342 | |||
086c4a243f | |||
cf21ee2647 | |||
1f6c8d0c33 | |||
bcbd909892 | |||
fc83650115 | |||
2524dfc8d3 | |||
477d009cb0 | |||
0a089db4ef | |||
0ad6a8cbfb | |||
77266e6c93 | |||
3fdf88f612 | |||
3ec07d62c3 | |||
424bbddaa3 | |||
64ebcadabb | |||
e1bf3661ec | |||
12736abcf7 | |||
b2d68d4b2d | |||
72cbf66395 | |||
04c7e0bb33 |
@ -532,6 +532,8 @@ AC_CONFIG_FILES([Makefile
|
||||
src/exchangedb/Makefile
|
||||
src/exchange-tools/Makefile
|
||||
src/extensions/Makefile
|
||||
src/extensions/age_restriction/Makefile
|
||||
src/extensions/policy_brandt_vickrey_auction/Makefile
|
||||
src/lib/Makefile
|
||||
src/kyclogic/Makefile
|
||||
src/testing/Makefile
|
||||
@ -544,4 +546,8 @@ AC_CONFIG_FILES([Makefile
|
||||
src/templating/Makefile
|
||||
src/util/Makefile
|
||||
])
|
||||
# TODO:
|
||||
# src/extensions/policy_null/Makefile
|
||||
# src/extensions/policy_merchant_refund/Makefile
|
||||
# src/extensions/policy_escrowed_payment/Makefile
|
||||
AC_OUTPUT
|
||||
|
@ -32,5 +32,7 @@ SUBDIRS = \
|
||||
auditor \
|
||||
lib \
|
||||
exchange-tools \
|
||||
extensions/age_restriction \
|
||||
extensions/policy_brandt_vickrey_auction \
|
||||
testing \
|
||||
benchmark
|
||||
|
@ -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",
|
||||
|
@ -113,7 +113,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_FULFILMENTS },
|
||||
{ .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS},
|
||||
{ .rt = TALER_EXCHANGEDB_RT_PURSE_REFUNDS},
|
||||
{ .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES},
|
||||
|
@ -1620,18 +1620,19 @@ deposit_cb (void *cls,
|
||||
want to do in parallel in the background to improve
|
||||
auditor performance! */
|
||||
if (GNUNET_OK !=
|
||||
TALER_wallet_deposit_verify (&deposit->amount_with_fee,
|
||||
&issue->fees.deposit,
|
||||
&h_wire,
|
||||
&deposit->h_contract_terms,
|
||||
&deposit->coin.h_age_commitment,
|
||||
NULL /* FIXME-Oec: #7270: h_extensions! */,
|
||||
&h_denom_pub,
|
||||
deposit->timestamp,
|
||||
&deposit->merchant_pub,
|
||||
deposit->refund_deadline,
|
||||
&deposit->coin.coin_pub,
|
||||
&deposit->csig))
|
||||
TALER_wallet_deposit_verify (
|
||||
&deposit->amount_with_fee,
|
||||
&issue->fees.deposit,
|
||||
&h_wire,
|
||||
&deposit->h_contract_terms,
|
||||
&deposit->coin.h_age_commitment,
|
||||
deposit->has_policy ? &deposit->h_policy : NULL,
|
||||
&h_denom_pub,
|
||||
deposit->timestamp,
|
||||
&deposit->merchant_pub,
|
||||
deposit->refund_deadline,
|
||||
&deposit->coin.coin_pub,
|
||||
&deposit->csig))
|
||||
{
|
||||
TALER_ARL_report (report_bad_sig_losses,
|
||||
GNUNET_JSON_PACK (
|
||||
|
@ -102,7 +102,7 @@ function cleanup()
|
||||
function exit_cleanup()
|
||||
{
|
||||
echo "Running exit-cleanup"
|
||||
if test ! -z ${POSTGRES_PATH:-}
|
||||
if ! [[ -z ${POSTGRES_PATH:-} ]]
|
||||
then
|
||||
echo "Stopping Postgres at ${POSTGRES_PATH}"
|
||||
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
|
||||
@ -2050,7 +2050,7 @@ INITDB_BIN=$(command -v initdb) || true
|
||||
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" 2> /dev/null | grep postgres | head -1` || exit_skip " MISSING"
|
||||
echo " FOUND at" `dirname $HAVE_INITDB`
|
||||
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
|
||||
fi
|
||||
|
@ -96,7 +96,7 @@ function cleanup()
|
||||
function exit_cleanup()
|
||||
{
|
||||
echo "Running exit-cleanup"
|
||||
if test ! -z ${POSTGRES_PATH:-}
|
||||
if [[ ! -z ${POSTGRES_PATH:-} ]]
|
||||
then
|
||||
echo "Stopping Postgres at ${POSTGRES_PATH}"
|
||||
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
|
||||
|
@ -32,7 +32,7 @@ function exit_fail() {
|
||||
|
||||
# Cleanup to run whenever we exit
|
||||
function cleanup() {
|
||||
if test ! -z ${POSTGRES_PATH:-}
|
||||
if ! [[ -z ${POSTGRES_PATH:-} ]]
|
||||
then
|
||||
${POSTGRES_PATH}/pg_ctl -D $TMPDIR stop &> /dev/null || true
|
||||
fi
|
||||
|
@ -245,7 +245,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 BIGSERIAL NOT NULL 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
|
||||
|
@ -195,7 +195,7 @@ setup_connection (struct PostgresClosure *pg)
|
||||
"INSERT INTO deposit_confirmations "
|
||||
"(master_pub"
|
||||
",h_contract_terms"
|
||||
",h_extensions"
|
||||
",h_policy"
|
||||
",h_wire"
|
||||
",exchange_timestamp"
|
||||
",wire_deadline"
|
||||
@ -214,7 +214,7 @@ setup_connection (struct PostgresClosure *pg)
|
||||
"SELECT"
|
||||
" serial_id"
|
||||
",h_contract_terms"
|
||||
",h_extensions"
|
||||
",h_policy"
|
||||
",h_wire"
|
||||
",exchange_timestamp"
|
||||
",wire_deadline"
|
||||
@ -1087,7 +1087,7 @@ postgres_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),
|
||||
@ -1168,8 +1168,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",
|
||||
|
@ -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.
|
||||
*/
|
||||
@ -2124,11 +2125,11 @@ extensions_cb (
|
||||
|
||||
|
||||
/**
|
||||
* Upload extension configuration
|
||||
* Upload extension manifests
|
||||
*
|
||||
* @param exchange_url base URL of the exchange
|
||||
* @param idx index of the operation we are performing (for logging)
|
||||
* @param value arguments for POSTing configurations of extensions
|
||||
* @param value arguments for POSTing manifests of extensions
|
||||
*/
|
||||
static void
|
||||
upload_extensions (const char *exchange_url,
|
||||
@ -2169,14 +2170,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;
|
||||
@ -2186,8 +2187,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))
|
||||
{
|
||||
@ -2386,6 +2387,7 @@ do_upload (char *const *args)
|
||||
return;
|
||||
}
|
||||
trigger_upload (CFG_exchange_url);
|
||||
|
||||
json_decref (out);
|
||||
out = NULL;
|
||||
}
|
||||
@ -3888,7 +3890,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 (
|
||||
@ -3900,14 +3902,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;
|
||||
}
|
||||
|
||||
@ -4248,18 +4250,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));
|
||||
@ -4268,46 +4276,49 @@ do_extensions_show (char *const *args)
|
||||
json_dumps (obj,
|
||||
JSON_INDENT (2)));
|
||||
json_decref (obj);
|
||||
next (args + 1);
|
||||
next (args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sign the configurations of the enabled extensions
|
||||
* Sign the manifests of the enabled extensions
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -4318,18 +4329,19 @@ do_extensions_sign (char *const *args)
|
||||
return;
|
||||
}
|
||||
|
||||
TALER_exchange_offline_extension_config_hash_sign (&h_config,
|
||||
&master_priv,
|
||||
&sig);
|
||||
TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests,
|
||||
&master_priv,
|
||||
&sig);
|
||||
obj = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_object_steal ("extensions",
|
||||
extensions),
|
||||
GNUNET_JSON_pack_data_auto (
|
||||
"extensions_sig",
|
||||
&sig));
|
||||
|
||||
output_operation (OP_EXTENSIONS,
|
||||
obj);
|
||||
next (args + 1);
|
||||
next (args);
|
||||
}
|
||||
|
||||
|
||||
@ -4396,6 +4408,7 @@ do_work_extensions (char *const *args)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (NULL == args[0])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
@ -4530,6 +4543,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,
|
||||
¤cy))
|
||||
@ -4538,18 +4569,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);
|
||||
@ -4579,7 +4598,6 @@ main (int argc,
|
||||
GNUNET_GETOPT_OPTION_END
|
||||
};
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
/* force linker to link against libtalerutil; if we do
|
||||
not do this, the linker may "optimize" libtalerutil
|
||||
away and skip #TALER_OS_init(), which we do need */
|
||||
|
@ -97,6 +97,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.
|
||||
*/
|
||||
@ -138,11 +146,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.
|
||||
*/
|
||||
@ -170,6 +173,7 @@ bool TEH_suicide;
|
||||
* TALER_SIGNATURE_MASTER_EXTENSION.
|
||||
*/
|
||||
struct TALER_MasterSignatureP TEH_extensions_sig;
|
||||
bool TEH_extensions_signed = false;
|
||||
|
||||
/**
|
||||
* Value to return from main()
|
||||
@ -1255,6 +1259,14 @@ handle_mhd_request (void *cls,
|
||||
.nargs = 4,
|
||||
.nargs_is_upper_bound = true
|
||||
},
|
||||
/* extensions endpoints */
|
||||
{
|
||||
.url = "extensions",
|
||||
.method = MHD_HTTP_METHOD_POST,
|
||||
.handler.post = &TEH_extensions_post_handler,
|
||||
.nargs = 4, /* Arbitrary upper bound */
|
||||
.nargs_is_upper_bound = true,
|
||||
},
|
||||
/* mark end of list */
|
||||
{
|
||||
.url = NULL
|
||||
|
@ -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
|
||||
|
@ -87,15 +87,33 @@ struct BatchDepositContext
|
||||
const char *payto_uri;
|
||||
|
||||
/**
|
||||
* Additional details for extensions relevant for this
|
||||
* Additional details for policy 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;
|
||||
|
||||
/**
|
||||
* Hash over @e policy_details, might be all zero;
|
||||
*/
|
||||
struct TALER_ExtensionPolicyHashP h_policy;
|
||||
|
||||
/**
|
||||
* If @e policy_json was present, the corresponding policy extension
|
||||
* calculates these details. These will be persisted in the policy_details
|
||||
* table.
|
||||
*/
|
||||
struct TALER_PolicyDetails policy_details;
|
||||
|
||||
/**
|
||||
* When @e policy_details are persisted, this contains the id of the record
|
||||
* in the policy_details table.
|
||||
*/
|
||||
uint64_t policy_details_serial_id;
|
||||
|
||||
/**
|
||||
* Time when this request was generated. Used, for example, to
|
||||
@ -173,7 +191,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,10 +260,27 @@ batch_deposit_transaction (void *cls,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct BatchDepositContext *dc = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
enum GNUNET_DB_QueryStatus qs = GNUNET_OK;
|
||||
bool balance_ok;
|
||||
bool in_conflict;
|
||||
|
||||
|
||||
/* If the deposit has a policy associated to it, persist it. This will
|
||||
* insert or update the record. */
|
||||
if (dc->has_policy)
|
||||
{
|
||||
qs = TEH_plugin->persist_policy_details (TEH_plugin->cls,
|
||||
&dc->policy_details,
|
||||
&dc->policy_details_serial_id,
|
||||
&dc->policy_details.
|
||||
accumulated_total,
|
||||
&dc->policy_details.
|
||||
fulfillment_state);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
}
|
||||
|
||||
/* deposit the individutal coins */
|
||||
for (unsigned int i = 0; i<dc->num_coins; i++)
|
||||
{
|
||||
const struct TALER_EXCHANGEDB_Deposit *deposit = &dc->deposits[i];
|
||||
@ -257,14 +292,19 @@ batch_deposit_transaction (void *cls,
|
||||
mhd_ret);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
qs = TEH_plugin->do_deposit (TEH_plugin->cls,
|
||||
deposit,
|
||||
known_coin_id,
|
||||
&dc->h_payto,
|
||||
false, /* FIXME-OEC: #7270 extension blocked */
|
||||
&dc->exchange_timestamp,
|
||||
&balance_ok,
|
||||
&in_conflict);
|
||||
|
||||
qs = TEH_plugin->do_deposit (
|
||||
TEH_plugin->cls,
|
||||
deposit,
|
||||
known_coin_id,
|
||||
&dc->h_payto,
|
||||
(dc->has_policy)
|
||||
? &dc->policy_details_serial_id
|
||||
: NULL,
|
||||
&dc->exchange_timestamp,
|
||||
&balance_ok,
|
||||
&in_conflict);
|
||||
|
||||
if (qs < 0)
|
||||
{
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
@ -469,18 +509,19 @@ parse_coin (struct MHD_Connection *connection,
|
||||
|
||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||
if (GNUNET_OK !=
|
||||
TALER_wallet_deposit_verify (&deposit->amount_with_fee,
|
||||
&deposit->deposit_fee,
|
||||
&dc->h_wire,
|
||||
&dc->h_contract_terms,
|
||||
&deposit->coin.h_age_commitment,
|
||||
&dc->h_extensions,
|
||||
&deposit->coin.denom_pub_hash,
|
||||
dc->timestamp,
|
||||
&dc->merchant_pub,
|
||||
dc->refund_deadline,
|
||||
&deposit->coin.coin_pub,
|
||||
&deposit->csig))
|
||||
TALER_wallet_deposit_verify (
|
||||
&deposit->amount_with_fee,
|
||||
&deposit->deposit_fee,
|
||||
&dc->h_wire,
|
||||
&dc->h_contract_terms,
|
||||
&deposit->coin.h_age_commitment,
|
||||
dc->has_policy ? &dc->h_policy : NULL,
|
||||
&deposit->coin.denom_pub_hash,
|
||||
dc->timestamp,
|
||||
&dc->merchant_pub,
|
||||
dc->refund_deadline,
|
||||
&deposit->coin.coin_pub,
|
||||
&deposit->csig))
|
||||
{
|
||||
TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
@ -496,11 +537,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 +553,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 +566,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 +599,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 +645,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 +688,33 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
|
||||
struct TALER_EXCHANGEDB_Deposit);
|
||||
for (unsigned int i = 0; i<dc.num_coins; i++)
|
||||
{
|
||||
if (GNUNET_OK !=
|
||||
(res = parse_coin (connection,
|
||||
json_array_get (coins,
|
||||
i),
|
||||
&dc,
|
||||
&dc.deposits[i])))
|
||||
do {
|
||||
res = parse_coin (connection,
|
||||
json_array_get (coins,
|
||||
i),
|
||||
&dc,
|
||||
&dc.deposits[i]);
|
||||
if (GNUNET_OK != res)
|
||||
break;
|
||||
|
||||
/* If applicable, accumulate all contributions into the policy_details */
|
||||
if (dc.has_policy)
|
||||
{
|
||||
/* FIXME: how doe 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);
|
||||
|
@ -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 extensions
|
||||
* @param h_contract_terms hash of contract details
|
||||
* @param exchange_timestamp exchange's timestamp
|
||||
* @param refund_deadline until when this deposit be refunded
|
||||
@ -61,7 +62,7 @@ reply_deposit_success (
|
||||
struct MHD_Connection *connection,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
const struct TALER_MerchantWireHashP *h_wire,
|
||||
const struct TALER_ExtensionContractHashP *h_extensions,
|
||||
const struct TALER_ExtensionPolicyHashP *h_policy,
|
||||
const struct TALER_PrivateContractHashP *h_contract_terms,
|
||||
struct GNUNET_TIME_Timestamp exchange_timestamp,
|
||||
struct GNUNET_TIME_Timestamp refund_deadline,
|
||||
@ -78,7 +79,7 @@ reply_deposit_success (
|
||||
&TEH_keys_exchange_sign_,
|
||||
h_contract_terms,
|
||||
h_wire,
|
||||
h_extensions,
|
||||
h_policy,
|
||||
exchange_timestamp,
|
||||
wire_deadline,
|
||||
refund_deadline,
|
||||
@ -131,6 +132,29 @@ struct DepositContext
|
||||
*/
|
||||
uint64_t known_coin_id;
|
||||
|
||||
/*
|
||||
* True if @e policy_json was provided
|
||||
*/
|
||||
bool has_policy;
|
||||
|
||||
/**
|
||||
* If @e has_policy is true, the corresponding policy extension calculates
|
||||
* these details. These will be persisted in the policy_details table.
|
||||
*/
|
||||
struct TALER_PolicyDetails policy_details;
|
||||
|
||||
/**
|
||||
* Hash over the policy data for this deposit (remains unknown to the
|
||||
* Exchange). Needed for the verification of the deposit's signature
|
||||
*/
|
||||
struct TALER_ExtensionPolicyHashP h_policy;
|
||||
|
||||
/**
|
||||
* When has_policy is true, and deposit->policy_details are
|
||||
* persisted, this contains the id of the record in the policy_details table.
|
||||
*/
|
||||
uint64_t policy_details_serial_id;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -163,14 +187,34 @@ deposit_transaction (void *cls,
|
||||
mhd_ret);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
qs = TEH_plugin->do_deposit (TEH_plugin->cls,
|
||||
dc->deposit,
|
||||
dc->known_coin_id,
|
||||
&dc->h_payto,
|
||||
false, /* FIXME-OEC: extension blocked #7270 */
|
||||
&dc->exchange_timestamp,
|
||||
&balance_ok,
|
||||
&in_conflict);
|
||||
|
||||
/* If the deposit has a policy associated to it, persist it. This will
|
||||
* insert or update the record. */
|
||||
if (dc->has_policy)
|
||||
{
|
||||
qs = TEH_plugin->persist_policy_details (
|
||||
TEH_plugin->cls,
|
||||
&dc->policy_details,
|
||||
&dc->policy_details_serial_id,
|
||||
&dc->policy_details.accumulated_total,
|
||||
&dc->policy_details.fulfillment_state);
|
||||
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
}
|
||||
|
||||
qs = TEH_plugin->do_deposit (
|
||||
TEH_plugin->cls,
|
||||
dc->deposit,
|
||||
dc->known_coin_id,
|
||||
&dc->h_payto,
|
||||
(dc->has_policy)
|
||||
? &dc->policy_details_serial_id
|
||||
: NULL,
|
||||
&dc->exchange_timestamp,
|
||||
&balance_ok,
|
||||
&in_conflict);
|
||||
|
||||
if (qs < 0)
|
||||
{
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
@ -216,6 +260,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),
|
||||
@ -228,8 +274,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
||||
&deposit.coin.denom_pub_hash),
|
||||
TALER_JSON_spec_denom_sig ("ub_sig",
|
||||
&deposit.coin.denom_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
|
||||
&deposit.merchant_pub),
|
||||
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
|
||||
&deposit.h_contract_terms),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
@ -240,12 +284,21 @@ 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_fixed_auto ("merchant_pub",
|
||||
&deposit.merchant_pub),
|
||||
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,10 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
||||
return MHD_YES; /* failure */
|
||||
}
|
||||
}
|
||||
|
||||
/* set the state of the policy presence in the deposit struct */
|
||||
dc.has_policy = ! no_policy_json;
|
||||
|
||||
/* validate merchant's wire details (as far as we can) */
|
||||
{
|
||||
char *emsg;
|
||||
@ -419,6 +476,27 @@ 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 +504,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 +559,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,
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
/**
|
||||
* @file taler-exchange-httpd_extensions.c
|
||||
* @brief Handle extensions (age-restriction, peer2peer)
|
||||
* @brief Handle extensions (age-restriction, policy extensions)
|
||||
* @author Özgür Kesim
|
||||
*/
|
||||
#include "platform.h"
|
||||
@ -77,65 +77,84 @@ extension_update_event_cb (void *cls,
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the config from the database as string
|
||||
// Get the manifest from the database as string
|
||||
{
|
||||
char *config_str = NULL;
|
||||
char *manifest_str = NULL;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
json_error_t err;
|
||||
json_t *config;
|
||||
json_t *manifest_js;
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
|
||||
extension->name,
|
||||
&config_str);
|
||||
qs = TEH_plugin->get_extension_manifest (TEH_plugin->cls,
|
||||
extension->name,
|
||||
&manifest_str);
|
||||
|
||||
if (qs < 0)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Couldn't get extension config\n");
|
||||
"Couldn't get extension manifest\n");
|
||||
GNUNET_break (0);
|
||||
return;
|
||||
}
|
||||
|
||||
// No config found -> disable extension
|
||||
if (NULL == config_str)
|
||||
if (NULL == manifest_str)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"No manifest found for extension %s, disabling it\n",
|
||||
extension->name);
|
||||
extension->disable ((struct TALER_Extension *) extension);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the string as JSON
|
||||
config = json_loads (config_str, JSON_DECODE_ANY, &err);
|
||||
if (NULL == config)
|
||||
manifest_js = json_loads (manifest_str, JSON_DECODE_ANY, &err);
|
||||
if (NULL == manifest_js)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to parse config for extension `%s' as JSON: %s (%s)\n",
|
||||
"Failed to parse manifest for extension `%s' as JSON: %s (%s)\n",
|
||||
extension->name,
|
||||
err.text,
|
||||
err.source);
|
||||
GNUNET_break (0);
|
||||
free (manifest_js);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the parser for the extension
|
||||
ret = extension->load_json_config (
|
||||
ret = extension->load_config (
|
||||
(struct TALER_Extension *) extension,
|
||||
config);
|
||||
json_object_get (manifest_js, "config"));
|
||||
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Couldn't parse configuration for extension %s from the database",
|
||||
extension->name);
|
||||
"Couldn't parse configuration for extension %s from the manifest in the database: %s\n",
|
||||
extension->name,
|
||||
manifest_str);
|
||||
GNUNET_break (0);
|
||||
}
|
||||
|
||||
free (manifest_str);
|
||||
json_decref (manifest_js);
|
||||
}
|
||||
|
||||
/* Special case age restriction: Update global flag and mask */
|
||||
if (TALER_Extension_AgeRestriction == type)
|
||||
{
|
||||
TEH_age_restriction_enabled =
|
||||
TALER_extensions_age_restriction_is_enabled ();
|
||||
const struct TALER_AgeRestrictionConfig *conf =
|
||||
TALER_extensions_get_age_restriction_config ();
|
||||
TEH_age_restriction_enabled = false;
|
||||
if (NULL != conf)
|
||||
{
|
||||
TEH_age_restriction_enabled = true;
|
||||
TEH_age_restriction_config = *conf;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"[age restriction] DB event has changed the config to %s with mask: %s\n",
|
||||
TEH_age_restriction_enabled ? "enabled": "DISABLED",
|
||||
TALER_age_mask_to_string (&conf->mask));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,14 +162,30 @@ extension_update_event_cb (void *cls,
|
||||
enum GNUNET_GenericReturnValue
|
||||
TEH_extensions_init ()
|
||||
{
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_extension_age_restriction_register ());
|
||||
|
||||
/* Set the event handler for updates */
|
||||
struct GNUNET_DB_EventHeaderP ev = {
|
||||
.size = htons (sizeof (ev)),
|
||||
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
|
||||
};
|
||||
|
||||
/* Load the shared libraries first */
|
||||
if (GNUNET_OK !=
|
||||
TALER_extensions_init (TEH_cfg))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"failed to load extensions");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
/* Check for age restriction */
|
||||
{
|
||||
const struct TALER_AgeRestrictionConfig *arc;
|
||||
|
||||
if (NULL !=
|
||||
(arc = TALER_extensions_get_age_restriction_config ()))
|
||||
TEH_age_restriction_config = *arc;
|
||||
}
|
||||
|
||||
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
|
||||
GNUNET_TIME_UNIT_FOREVER_REL,
|
||||
&ev,
|
||||
@ -162,17 +197,24 @@ TEH_extensions_init ()
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
/* FIXME #7270: shall we load the extensions from the config right away?
|
||||
* We do have to for now, as otherwise denominations with age restriction
|
||||
* will not have the age mask set right upon initial generation.
|
||||
*/
|
||||
TALER_extensions_load_taler_config (TEH_cfg);
|
||||
|
||||
/* Trigger the initial load of configuration from the db */
|
||||
for (const struct TALER_Extension *it = TALER_extensions_get_head ();
|
||||
NULL != it->next;
|
||||
for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
|
||||
NULL != it && NULL != it->extension;
|
||||
it = it->next)
|
||||
extension_update_event_cb (NULL, &it->type, sizeof(it->type));
|
||||
{
|
||||
const struct TALER_Extension *ext = it->extension;
|
||||
uint32_t typ = htonl (ext->type);
|
||||
char *manifest = json_dumps (ext->manifest (ext), JSON_COMPACT);
|
||||
|
||||
TEH_plugin->set_extension_manifest (TEH_plugin->cls,
|
||||
ext->name,
|
||||
manifest);
|
||||
|
||||
extension_update_event_cb (NULL,
|
||||
&typ,
|
||||
sizeof(typ));
|
||||
free (manifest);
|
||||
}
|
||||
|
||||
return GNUNET_OK;
|
||||
}
|
||||
@ -190,4 +232,168 @@ TEH_extensions_done ()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief Execute database transactions for /extensions/policy_* POST requests.
|
||||
*
|
||||
* @param cls a `struct TALER_PolicyFulfillmentOutcome`
|
||||
* @param connection MHD request context
|
||||
* @param[out] mhd_ret set to MHD status on error
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
policy_fulfillment_transaction (
|
||||
void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct TALER_PolicyFulfillmentTransactionData *fulfillment = cls;
|
||||
|
||||
return TEH_plugin->add_policy_fulfillment_proof (TEH_plugin->cls,
|
||||
fulfillment);
|
||||
}
|
||||
|
||||
|
||||
MHD_RESULT
|
||||
TEH_extensions_post_handler (
|
||||
struct TEH_RequestContext *rc,
|
||||
const json_t *root,
|
||||
const char *const args[])
|
||||
{
|
||||
const struct TALER_Extension *ext = NULL;
|
||||
json_t *output;
|
||||
struct TALER_PolicyDetails *policy_details = NULL;
|
||||
size_t policy_details_count = 0;
|
||||
|
||||
|
||||
if (NULL == args[0])
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
||||
"/extensions/$EXTENSION");
|
||||
}
|
||||
|
||||
ext = TALER_extensions_get_by_name (args[0]);
|
||||
if (NULL == ext)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
||||
"/extensions/$EXTENSION unknown");
|
||||
}
|
||||
|
||||
if (NULL == ext->policy_post_handler)
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_NOT_IMPLEMENTED,
|
||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
||||
"POST /extensions/$EXTENSION not supported");
|
||||
|
||||
/* Extract hash_codes and retrieve related policy_details from the DB */
|
||||
{
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
const char *error_msg;
|
||||
struct GNUNET_HashCode *hcs;
|
||||
size_t len;
|
||||
json_t*val;
|
||||
size_t idx;
|
||||
json_t *jhash_codes = json_object_get (root,
|
||||
"policy_hash_codes");
|
||||
if (! json_is_array (jhash_codes))
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
||||
"policy_hash_codes are missing");
|
||||
|
||||
len = json_array_size (jhash_codes);
|
||||
hcs = GNUNET_new_array (len,
|
||||
struct GNUNET_HashCode);
|
||||
policy_details = GNUNET_new_array (len,
|
||||
struct TALER_PolicyDetails);
|
||||
|
||||
json_array_foreach (jhash_codes, idx, val)
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto (NULL, &hcs[idx]),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
ret = GNUNET_JSON_parse (val,
|
||||
spec,
|
||||
&error_msg,
|
||||
NULL);
|
||||
if (GNUNET_OK != ret)
|
||||
break;
|
||||
|
||||
qs = TEH_plugin->get_policy_details (TEH_plugin->cls,
|
||||
&hcs[idx],
|
||||
&policy_details[idx]);
|
||||
if (qs < 0)
|
||||
{
|
||||
error_msg = "a policy_hash_code couldn't be found";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GNUNET_free (hcs);
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
GNUNET_free (policy_details);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
||||
error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
ret = ext->policy_post_handler (root,
|
||||
&args[1],
|
||||
policy_details,
|
||||
policy_details_count,
|
||||
&output);
|
||||
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
TALER_MHD_reply_json_steal (
|
||||
rc->connection,
|
||||
output,
|
||||
MHD_HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/* execute fulfillment transaction */
|
||||
{
|
||||
MHD_RESULT mhd_ret;
|
||||
struct TALER_PolicyFulfillmentTransactionData fulfillment = {
|
||||
.proof = root,
|
||||
.timestamp = GNUNET_TIME_timestamp_get (),
|
||||
.details = policy_details,
|
||||
.details_count = policy_details_count
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TEH_DB_run_transaction (rc->connection,
|
||||
"execute policy fulfillment",
|
||||
TEH_MT_REQUEST_POLICY_FULFILMENT,
|
||||
&mhd_ret,
|
||||
&policy_fulfillment_transaction,
|
||||
&fulfillment))
|
||||
{
|
||||
json_decref (output);
|
||||
return mhd_ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TALER_MHD_reply_json_steal (rc->connection,
|
||||
output,
|
||||
MHD_HTTP_OK);
|
||||
}
|
||||
|
||||
|
||||
/* end of taler-exchange-httpd_extensions.c */
|
||||
|
@ -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
|
||||
|
@ -815,10 +815,7 @@ static struct TALER_AgeMask
|
||||
load_age_mask (const char*section_name)
|
||||
{
|
||||
static const struct TALER_AgeMask null_mask = {0};
|
||||
struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask ();
|
||||
|
||||
if (age_mask.bits == 0)
|
||||
return null_mask;
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
|
||||
TEH_cfg,
|
||||
@ -826,22 +823,29 @@ load_age_mask (const char*section_name)
|
||||
"AGE_RESTRICTED")))
|
||||
return null_mask;
|
||||
|
||||
if (GNUNET_SYSERR ==
|
||||
(ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
|
||||
section_name,
|
||||
"AGE_RESTRICTED")))
|
||||
{
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
if (GNUNET_SYSERR ==
|
||||
(ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
|
||||
section_name,
|
||||
"AGE_RESTRICTED")))
|
||||
{
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
section_name,
|
||||
"AGE_RESTRICTED",
|
||||
"Value must be YES or NO\n");
|
||||
return null_mask;
|
||||
}
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
section_name,
|
||||
"AGE_RESTRICTED",
|
||||
"Value must be YES or NO\n");
|
||||
return null_mask;
|
||||
}
|
||||
return age_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 null_mask;
|
||||
}
|
||||
|
||||
|
||||
@ -1898,41 +1902,29 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
||||
bool has_extensions = false;
|
||||
|
||||
/* Fill in the configurations of the enabled extensions */
|
||||
for (const struct TALER_Extension *extension = TALER_extensions_get_head ();
|
||||
NULL != extension;
|
||||
extension = extension->next)
|
||||
for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
|
||||
NULL != iter && NULL != iter->extension;
|
||||
iter = iter->next)
|
||||
{
|
||||
json_t *ext;
|
||||
json_t *config_json;
|
||||
const struct TALER_Extension *extension = iter->extension;
|
||||
json_t *manifest;
|
||||
int r;
|
||||
|
||||
/* skip if not configured == disabled */
|
||||
if (NULL == extension->config ||
|
||||
NULL == extension->config_json)
|
||||
/* skip if not enabled */
|
||||
if (! extension->enabled)
|
||||
continue;
|
||||
|
||||
/* flag our findings so far */
|
||||
has_extensions = true;
|
||||
|
||||
GNUNET_assert (NULL != extension->config_json);
|
||||
|
||||
config_json = json_copy (extension->config_json);
|
||||
GNUNET_assert (NULL != config_json);
|
||||
|
||||
ext = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_bool ("critical",
|
||||
extension->critical),
|
||||
GNUNET_JSON_pack_string ("version",
|
||||
extension->version),
|
||||
GNUNET_JSON_pack_object_steal ("config",
|
||||
config_json)
|
||||
);
|
||||
GNUNET_assert (NULL != ext);
|
||||
manifest = extension->manifest (extension);
|
||||
GNUNET_assert (manifest);
|
||||
|
||||
r = json_object_set_new (
|
||||
extensions,
|
||||
extension->name,
|
||||
ext);
|
||||
manifest);
|
||||
GNUNET_assert (0 == r);
|
||||
}
|
||||
|
||||
@ -1948,12 +1940,16 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
||||
extensions);
|
||||
GNUNET_assert (0 == r);
|
||||
|
||||
sig = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_data_auto ("extensions_sig",
|
||||
&TEH_extensions_sig));
|
||||
/* Add the signature of the extensions, if it is not zero */
|
||||
if (TEH_extensions_signed)
|
||||
{
|
||||
sig = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_data_auto ("extensions_sig",
|
||||
&TEH_extensions_sig));
|
||||
|
||||
r = json_object_update (keys, sig);
|
||||
GNUNET_assert (0 == r);
|
||||
r = json_object_update (keys, sig);
|
||||
GNUNET_assert (0 == r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2560,6 +2556,7 @@ build_key_state (struct HelperState *hs,
|
||||
true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */
|
||||
qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls,
|
||||
&signkey_info_cb,
|
||||
|
@ -80,7 +80,7 @@ set_extensions (void *cls,
|
||||
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->config, 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,14 +176,14 @@ 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;
|
||||
@ -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))
|
||||
{
|
||||
|
@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest
|
||||
TEH_MT_REQUEST_IDEMPOTENT_MELT = 10,
|
||||
TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11,
|
||||
TEH_MT_REQUEST_BATCH_DEPOSIT = 12,
|
||||
TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */
|
||||
TEH_MT_REQUEST_POLICY_FULFILMENT = 13,
|
||||
TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -76,7 +76,7 @@ TEH_RESPONSE_compile_transaction_history (
|
||||
&h_wire,
|
||||
&deposit->h_contract_terms,
|
||||
&deposit->h_age_commitment,
|
||||
NULL /* h_extensions! */,
|
||||
(deposit->has_policy) ? &deposit->h_policy : NULL,
|
||||
&deposit->h_denom_pub,
|
||||
deposit->timestamp,
|
||||
&deposit->merchant_pub,
|
||||
@ -110,6 +110,10 @@ TEH_RESPONSE_compile_transaction_history (
|
||||
&deposit->h_contract_terms),
|
||||
GNUNET_JSON_pack_data_auto ("h_wire",
|
||||
&h_wire),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_data_auto (
|
||||
"h_policy",
|
||||
((deposit->has_policy) ? &deposit->h_policy : NULL))),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
deposit->no_age_commitment ?
|
||||
GNUNET_JSON_pack_string (
|
||||
|
@ -885,8 +885,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)'
|
||||
@ -2617,7 +2617,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
|
||||
;
|
||||
|
||||
|
@ -432,14 +432,14 @@ COMMENT ON TABLE signkey_revocations
|
||||
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';
|
||||
|
||||
|
||||
-- ------------------------------ known_coins ----------------------------------------
|
||||
@ -536,21 +536,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 ----------------------------------------
|
||||
|
||||
@ -568,10 +616,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 policy extensions are not used';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS deposits_default
|
||||
PARTITION OF deposits
|
||||
@ -607,7 +655,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
|
||||
@ -651,8 +699,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
|
||||
@ -706,7 +754,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
|
||||
|
@ -669,11 +669,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
|
||||
};
|
||||
|
||||
@ -905,9 +905,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
|
||||
};
|
||||
|
||||
@ -918,27 +918,34 @@ irbt_cb_table_extensions (struct PostgresClosure *pg,
|
||||
|
||||
|
||||
/**
|
||||
* Function called with extension_details records to insert into table.
|
||||
* Function called with policy_details records to insert into table.
|
||||
*
|
||||
* @param pg plugin context
|
||||
* @param td record to insert
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
irbt_cb_table_extension_details (struct PostgresClosure *pg,
|
||||
const struct TALER_EXCHANGEDB_TableData *td)
|
||||
irbt_cb_table_policy_details (struct PostgresClosure *pg,
|
||||
const struct TALER_EXCHANGEDB_TableData *td)
|
||||
{
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_uint64 (&td->serial),
|
||||
NULL ==
|
||||
td->details.extension_details.extension_options ?
|
||||
GNUNET_PQ_query_param_null () :
|
||||
GNUNET_PQ_query_param_string (
|
||||
td->details.extension_details.extension_options),
|
||||
((NULL == td->details.policy_details.policy_json) ||
|
||||
(td->details.policy_details.no_policy_json))
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: TALER_PQ_query_param_json (td->details.policy_details.policy_json),
|
||||
GNUNET_PQ_query_param_timestamp (
|
||||
&td->details.policy_details.deadline),
|
||||
GNUNET_PQ_query_param_uint16 (
|
||||
&td->details.policy_details.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
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"insert_into_table_extension_details",
|
||||
"insert_into_table_policy_details",
|
||||
params);
|
||||
}
|
||||
|
||||
|
@ -895,7 +895,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",
|
||||
@ -940,13 +940,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
|
||||
};
|
||||
|
||||
@ -1400,8 +1400,8 @@ 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),
|
||||
GNUNET_PQ_result_spec_string ("manifest",
|
||||
&td.details.extensions.manifest),
|
||||
&no_config),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
@ -1423,32 +1423,111 @@ lrbt_cb_table_extensions (void *cls,
|
||||
|
||||
|
||||
/**
|
||||
* Function called with extension_details table entries.
|
||||
* Function called with policy_details table entries.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param result the postgres result
|
||||
* @param num_results the number of results in @a result
|
||||
*/
|
||||
static void
|
||||
lrbt_cb_table_extension_details (void *cls,
|
||||
PGresult *result,
|
||||
unsigned int num_results)
|
||||
lrbt_cb_table_policy_details (void *cls,
|
||||
PGresult *result,
|
||||
unsigned int num_results)
|
||||
{
|
||||
struct LookupRecordsByTableContext *ctx = cls;
|
||||
struct PostgresClosure *pg = ctx->pg;
|
||||
struct TALER_EXCHANGEDB_TableData td = {
|
||||
.table = TALER_EXCHANGEDB_RT_POLICY_DETAILS
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i<num_results; i++)
|
||||
{
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
|
||||
&td.serial),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("hash_code",
|
||||
&td.details.policy_details.
|
||||
hash_code),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
TALER_PQ_result_spec_json ("policy_json",
|
||||
&td.details.policy_details.
|
||||
policy_json),
|
||||
&td.details.policy_details.no_policy_json),
|
||||
GNUNET_PQ_result_spec_timestamp ("deadline",
|
||||
&td.details.policy_details.
|
||||
deadline),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
|
||||
&td.details.policy_details.
|
||||
commitment),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
|
||||
&td.details.policy_details.
|
||||
accumulated_total),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee",
|
||||
&td.details.policy_details.
|
||||
fee),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("transferable",
|
||||
&td.details.policy_details.
|
||||
transferable),
|
||||
GNUNET_PQ_result_spec_uint16 ("fulfillment_state",
|
||||
&td.details.policy_details.
|
||||
fulfillment_state),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
|
||||
&td.details.policy_details.
|
||||
fulfillment_id),
|
||||
&td.details.policy_details.no_fulfillment_id),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_PQ_extract_result (result,
|
||||
rs,
|
||||
i))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
ctx->error = true;
|
||||
return;
|
||||
}
|
||||
ctx->cb (ctx->cb_cls,
|
||||
&td);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called with policy_fulfillments table entries.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param result the postgres result
|
||||
* @param num_results the number of results in @a result
|
||||
*/
|
||||
static void
|
||||
lrbt_cb_table_policy_fulfillments (void *cls,
|
||||
PGresult *result,
|
||||
unsigned int num_results)
|
||||
{
|
||||
struct LookupRecordsByTableContext *ctx = cls;
|
||||
struct TALER_EXCHANGEDB_TableData td = {
|
||||
.table = TALER_EXCHANGEDB_RT_EXTENSION_DETAILS
|
||||
.table = TALER_EXCHANGEDB_RT_POLICY_FULFILMENTS
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i<num_results; i++)
|
||||
{
|
||||
bool no_config = 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),
|
||||
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_config),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
@ -2993,11 +2993,11 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" LIMIT 1;",
|
||||
0),
|
||||
GNUNET_PQ_make_prepare (
|
||||
"select_serial_by_table_extension_details",
|
||||
"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;",
|
||||
0),
|
||||
GNUNET_PQ_make_prepare (
|
||||
@ -3318,8 +3318,8 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
",wire_salt"
|
||||
",wire_target_h_payto"
|
||||
",done"
|
||||
",extension_blocked"
|
||||
",extension_details_serial_id"
|
||||
",policy_blocked"
|
||||
",policy_details_serial_id"
|
||||
" FROM deposits"
|
||||
" WHERE deposit_serial_id > $1"
|
||||
" ORDER BY deposit_serial_id ASC;",
|
||||
@ -3821,8 +3821,8 @@ prepare_statements (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);",
|
||||
@ -3922,22 +3922,50 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
"INSERT INTO extensions"
|
||||
"(extension_id"
|
||||
",name"
|
||||
",config"
|
||||
",manifest"
|
||||
") VALUES "
|
||||
"($1, $2, $3);",
|
||||
3),
|
||||
GNUNET_PQ_make_prepare (
|
||||
"insert_into_table_extension_details",
|
||||
"INSERT INTO extension_details"
|
||||
"(extension_details_serial_id"
|
||||
",extension_options"
|
||||
"call_insert_or_update_policy_details",
|
||||
"SELECT "
|
||||
" out_policy_details_serial_id as policy_serial_id"
|
||||
",out_accumulated_total_val as accumulated_total_val"
|
||||
",out_accumulated_total_frac as accumulated_total_frac"
|
||||
",out_fulfillment_state as fulfillment_state"
|
||||
" FROM insert_or_update_policy_details"
|
||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);",
|
||||
12),
|
||||
GNUNET_PQ_make_prepare (
|
||||
"insert_proof_into_policy_fulfillments",
|
||||
"INSERT INTO policy_fulfillments "
|
||||
"(fulfillment_timestamp "
|
||||
",fulfillment_proof"
|
||||
",h_fulfillment_proof"
|
||||
",policy_hash_codes"
|
||||
") VALUES "
|
||||
"($1, $2);",
|
||||
2),
|
||||
|
||||
"($1, $2, $3, $4) "
|
||||
"RETURNING fulfillment_id;",
|
||||
4),
|
||||
GNUNET_PQ_make_prepare (
|
||||
"update_policy_details",
|
||||
"UPDATE policy_details "
|
||||
"SET "
|
||||
" deadline=$2,"
|
||||
" commitment_val=$3,"
|
||||
" commitment_frac=$4,"
|
||||
" accumulated_total_val=$5,"
|
||||
" accumulated_total_frac=$6,"
|
||||
" fee_val=$7,"
|
||||
" fee_frac=$8,"
|
||||
" transferable_val=$9,"
|
||||
" transferable_frac=$10,"
|
||||
" fulfillment_state=$11 "
|
||||
"WHERE policy_hash_code=$1;",
|
||||
11),
|
||||
GNUNET_PQ_make_prepare (
|
||||
"insert_into_table_purse_requests",
|
||||
"INSERT INTO purse_requests"
|
||||
"INSERT INTO purse_requests "
|
||||
"(purse_requests_serial_id"
|
||||
",purse_pub"
|
||||
",merge_pub"
|
||||
@ -4206,18 +4234,18 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" AND start_row=$2"
|
||||
" AND end_row=$3",
|
||||
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",
|
||||
"DO UPDATE SET manifest=$2",
|
||||
2),
|
||||
/* Used in #postgres_get_extension_config */
|
||||
/* Used in #postgres_get_extension_manifest */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"get_extension_config",
|
||||
"get_extension_manifest",
|
||||
"SELECT "
|
||||
" config "
|
||||
" manifest "
|
||||
"FROM extensions"
|
||||
" WHERE name=$1;",
|
||||
1),
|
||||
@ -6230,7 +6258,7 @@ compute_shard (const struct TALER_MerchantPublicKeyP *merchant_pub)
|
||||
* @param deposit deposit operation details
|
||||
* @param known_coin_id row of the coin in the known_coins table
|
||||
* @param h_payto hash of the merchant's bank account details
|
||||
* @param extension_blocked true if an extension is blocking the wire transfer
|
||||
* @param policy_details_serial_id (pointer to) the row ID in the policy_details table, maybe NULL.
|
||||
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] in_conflict set to true if the deposit conflicted
|
||||
@ -6242,7 +6270,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)
|
||||
@ -6264,10 +6292,10 @@ postgres_do_deposit (
|
||||
GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
|
||||
GNUNET_PQ_query_param_uint64 (&deposit_shard),
|
||||
GNUNET_PQ_query_param_bool (extension_blocked),
|
||||
(NULL == deposit->extension_details)
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: TALER_PQ_query_param_json (deposit->extension_details),
|
||||
GNUNET_PQ_query_param_bool (deposit->has_policy),
|
||||
(NULL == policy_details_serial_id)
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_uint64 (policy_details_serial_id),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
@ -6287,6 +6315,101 @@ postgres_do_deposit (
|
||||
}
|
||||
|
||||
|
||||
/* Get the details of a policy, referenced by its hash code
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param hc The hash code under which the details to a particular policy should be found
|
||||
* @param[out] details The found details
|
||||
* @return query execution status
|
||||
* */
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_get_policy_details (
|
||||
void *cls,
|
||||
const struct GNUNET_HashCode *hc,
|
||||
struct TALER_PolicyDetails *details)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (hc),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_timestamp ("deadline",
|
||||
&details->deadline),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("commitment",
|
||||
&details->commitment),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
|
||||
&details->accumulated_total),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("policy_fee",
|
||||
&details->policy_fee),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("transferable_amount",
|
||||
&details->transferable_amount),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("state",
|
||||
&details->fulfillment_state),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
GNUNET_PQ_result_spec_uint64 ("policy_fulfillment_id",
|
||||
&details->policy_fulfillment_id),
|
||||
&details->no_policy_fulfillment_id),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_policy_details",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
/* Persist the details to a policy in the policy_details table. If there
|
||||
* already exists a policy, update the fields accordingly.
|
||||
*
|
||||
* @param details The policy details that should be persisted. If an entry for
|
||||
* the given details->hash_code exists, the values will be updated.
|
||||
* @param[out] policy_details_serial_id The row ID of the policy details
|
||||
* @param[out] accumulated_total The total amount accumulated in that policy
|
||||
* @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready.
|
||||
* @return query execution status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_persist_policy_details (
|
||||
void *cls,
|
||||
const struct TALER_PolicyDetails *details,
|
||||
uint64_t *policy_details_serial_id,
|
||||
struct TALER_Amount *accumulated_total,
|
||||
enum TALER_PolicyFulfillmentState *fulfillment_state)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (&details->hash_code),
|
||||
TALER_PQ_query_param_json (details->policy_json),
|
||||
GNUNET_PQ_query_param_timestamp (&details->deadline),
|
||||
TALER_PQ_query_param_amount (&details->commitment),
|
||||
TALER_PQ_query_param_amount (&details->accumulated_total),
|
||||
TALER_PQ_query_param_amount (&details->policy_fee),
|
||||
TALER_PQ_query_param_amount (&details->transferable_amount),
|
||||
GNUNET_PQ_query_param_auto_from_type (&details->fulfillment_state),
|
||||
(details->no_policy_fulfillment_id)
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_uint64 (&details->policy_fulfillment_id),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id",
|
||||
policy_details_serial_id),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total",
|
||||
accumulated_total),
|
||||
GNUNET_PQ_result_spec_uint32 ("fulfillment_state",
|
||||
fulfillment_state),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_insert_or_update_policy_details",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform melt operation, checking for sufficient balance
|
||||
* of the coin and possibly persisting the melt details.
|
||||
@ -6541,6 +6664,118 @@ postgres_do_recoup_refresh (
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compares two indices into an array of hash codes according to
|
||||
* GNUNET_CRYPTO_hash_cmp of the content at those index positions.
|
||||
*
|
||||
* Used in a call qsort_t in order to generate sorted policy_hash_codes.
|
||||
*/
|
||||
static int
|
||||
hash_code_cmp (
|
||||
const void *hc1,
|
||||
const void *hc2,
|
||||
void *arg)
|
||||
{
|
||||
size_t i1 = *(size_t *) hc1;
|
||||
size_t i2 = *(size_t *) hc2;
|
||||
const struct TALER_PolicyDetails *d = arg;
|
||||
|
||||
return GNUNET_CRYPTO_hash_cmp (&d[i1].hash_code,
|
||||
&d[i2].hash_code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a proof of fulfillment into the policy_fulfillments table
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param[out] proof_id set record id for the proof
|
||||
* @return query execution status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_add_policy_fulfillment_proof (
|
||||
void *cls,
|
||||
struct TALER_PolicyFulfillmentTransactionData *fulfillment)
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct PostgresClosure *pg = cls;
|
||||
size_t count = fulfillment->details_count;
|
||||
struct GNUNET_HashCode hcs[count];
|
||||
|
||||
/* Create the sorted policy_hash_codes */
|
||||
{
|
||||
size_t idx[count];
|
||||
for (size_t i = 0; i < count; i++)
|
||||
idx[i] = i;
|
||||
|
||||
/* Sort the indices according to the hash codes of the corresponding
|
||||
* details. */
|
||||
qsort_r (idx,
|
||||
count,
|
||||
sizeof(size_t),
|
||||
hash_code_cmp,
|
||||
fulfillment->details);
|
||||
|
||||
/* Finally, concatenate all hash_codes in sorted order */
|
||||
for (size_t i = 0; i < count; i++)
|
||||
hcs[i] = fulfillment->details[idx[i]].hash_code;
|
||||
}
|
||||
|
||||
|
||||
/* Now, add the proof to the policy_fulfillments table, retrieve the
|
||||
* record_id */
|
||||
{
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_timestamp (&fulfillment->timestamp),
|
||||
TALER_PQ_query_param_json (fulfillment->proof),
|
||||
GNUNET_PQ_query_param_auto_from_type (&fulfillment->h_proof),
|
||||
GNUNET_PQ_query_param_fixed_size (hcs,
|
||||
count * sizeof(struct GNUNET_HashCode)),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("fulfillment_id",
|
||||
&fulfillment->fulfillment_id),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"insert_proof_into_policy_fulfillments",
|
||||
params,
|
||||
rs);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
||||
return qs;
|
||||
}
|
||||
|
||||
/* Now, set the states of each entry corresponding to the hash_codes in
|
||||
* policy_details accordingly */
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
struct TALER_PolicyDetails *pos = &fulfillment->details[i];
|
||||
{
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (&pos->hash_code),
|
||||
GNUNET_PQ_query_param_timestamp (&pos->deadline),
|
||||
TALER_PQ_query_param_amount (&pos->commitment),
|
||||
TALER_PQ_query_param_amount (&pos->accumulated_total),
|
||||
TALER_PQ_query_param_amount (&pos->policy_fee),
|
||||
TALER_PQ_query_param_amount (&pos->transferable_amount),
|
||||
GNUNET_PQ_query_param_auto_from_type (&pos->fulfillment_state),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"update_policy_details",
|
||||
params);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
}
|
||||
}
|
||||
|
||||
return qs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closure for callbacks invoked via #postgres_get_reserve_history.
|
||||
*/
|
||||
@ -14225,8 +14460,8 @@ postgres_lookup_serial_by_table (void *cls,
|
||||
case TALER_EXCHANGEDB_RT_EXTENSIONS:
|
||||
statement = "select_serial_by_table_extensions";
|
||||
break;
|
||||
case TALER_EXCHANGEDB_RT_EXTENSION_DETAILS:
|
||||
statement = "select_serial_by_table_extension_details";
|
||||
case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
|
||||
statement = "select_serial_by_table_policy_details";
|
||||
break;
|
||||
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
|
||||
statement = "select_serial_by_table_purse_requests";
|
||||
@ -14437,9 +14672,13 @@ postgres_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_FULFILMENTS:
|
||||
statement = "select_above_serial_by_table_policy_fulfillments";
|
||||
rh = &lrbt_cb_table_policy_fulfillments;
|
||||
break;
|
||||
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
|
||||
statement = "select_above_serial_by_table_purse_requests";
|
||||
@ -14620,9 +14859,10 @@ postgres_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;
|
||||
/* TODO: policy_details_fulfillments and policy_fulfillments */
|
||||
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
|
||||
rh = &irbt_cb_table_purse_requests;
|
||||
break;
|
||||
@ -15199,25 +15439,25 @@ postgres_delete_shard_locks (void *cls)
|
||||
|
||||
|
||||
/**
|
||||
* Function called to save the configuration of an extension
|
||||
* (age-restriction, peer2peer, ...). After successful storage of the
|
||||
* configuration it triggers the corresponding event.
|
||||
* Function called to save the manifest of an extension
|
||||
* (age_restriction, poliy_merchant_refund, ...). After successful storage of
|
||||
* the manifest it triggers the corresponding event.
|
||||
*
|
||||
* @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
|
||||
* @param manifest JSON object of the manifest as string
|
||||
* @return transaction status code
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
postgres_set_extension_config (void *cls,
|
||||
const char *extension_name,
|
||||
const char *config)
|
||||
postgres_set_extension_manifest (void *cls,
|
||||
const char *extension_name,
|
||||
const char *manifest)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam pcfg =
|
||||
(NULL == config || 0 == *config)
|
||||
(NULL == manifest || 0 == *manifest)
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_string (config);
|
||||
: GNUNET_PQ_query_param_string (manifest);
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_string (extension_name),
|
||||
pcfg,
|
||||
@ -15225,24 +15465,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_merchant_refund, ...)
|
||||
*
|
||||
* @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 Manifest of the extension encoded as JSON object
|
||||
* @return transaction status code
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
postgres_get_extension_config (void *cls,
|
||||
const char *extension_name,
|
||||
char **config)
|
||||
postgres_get_extension_manifest (void *cls,
|
||||
const char *extension_name,
|
||||
char **manifest)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
@ -15252,15 +15492,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);
|
||||
}
|
||||
@ -17077,7 +17317,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
||||
plugin->do_withdraw = &postgres_do_withdraw;
|
||||
plugin->do_batch_withdraw = &postgres_do_batch_withdraw;
|
||||
plugin->do_batch_withdraw_insert = &postgres_do_batch_withdraw_insert;
|
||||
plugin->get_policy_details = &postgres_get_policy_details;
|
||||
plugin->persist_policy_details = &postgres_persist_policy_details;
|
||||
plugin->do_deposit = &postgres_do_deposit;
|
||||
plugin->add_policy_fulfillment_proof = &postgres_add_policy_fulfillment_proof;
|
||||
plugin->do_melt = &postgres_do_melt;
|
||||
plugin->do_refund = &postgres_do_refund;
|
||||
plugin->do_recoup = &postgres_do_recoup;
|
||||
@ -17231,10 +17474,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
|
||||
|
@ -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,11 @@ 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;
|
||||
-- Shards: INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING;
|
||||
-- INSERT deposits (by coin_pub, shard), ON CONFLICT DO NOTHING;
|
||||
-- UPDATE known_coins (by coin_pub)
|
||||
|
||||
IF NOT NULL in_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 +557,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 +575,9 @@ 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
|
||||
@ -2200,5 +2187,119 @@ BEGIN
|
||||
|
||||
END $$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION insert_or_update_policy_details(
|
||||
IN in_policy_hash_code BYTEA,
|
||||
IN in_policy_json VARCHAR,
|
||||
IN in_deadline INT8,
|
||||
IN in_commitment_val INT8,
|
||||
IN in_commitment_frac INT4,
|
||||
IN in_accumulated_total_val INT8,
|
||||
IN in_accumulated_total_frac INT4,
|
||||
IN in_fee_val INT8,
|
||||
IN in_fee_frac INT4,
|
||||
IN in_transferable_val INT8,
|
||||
IN in_transferable_frac INT4,
|
||||
IN in_fulfillment_state SMALLINT,
|
||||
OUT out_policy_details_serial_id INT8,
|
||||
OUT out_accumulated_total_val INT8,
|
||||
OUT out_accumulated_total_frac INT4,
|
||||
OUT out_fulfillment_state SMALLINT)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
cur_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;
|
||||
|
@ -112,53 +112,53 @@ mark_prepare_cb (void *cls,
|
||||
* Simple check that config retrieval and setting for extensions work
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
test_extension_config (void)
|
||||
test_extension_manifest (void)
|
||||
{
|
||||
char *config;
|
||||
char *manifest;
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
||||
plugin->get_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
&config));
|
||||
plugin->get_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
&manifest));
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->set_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
"bar"));
|
||||
plugin->set_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
"bar"));
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->get_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
&config));
|
||||
plugin->get_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
&manifest));
|
||||
|
||||
FAILIF (0 != strcmp ("bar", config));
|
||||
GNUNET_free (config);
|
||||
FAILIF (0 != strcmp ("bar", manifest));
|
||||
GNUNET_free (manifest);
|
||||
|
||||
/* let's do this again! */
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->set_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
"buzz"));
|
||||
plugin->set_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
"buzz"));
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->get_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
&config));
|
||||
plugin->get_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
&manifest));
|
||||
|
||||
FAILIF (0 != strcmp ("buzz", config));
|
||||
GNUNET_free (config);
|
||||
FAILIF (0 != strcmp ("buzz", manifest));
|
||||
GNUNET_free (manifest);
|
||||
|
||||
/* let's do this again, with NULL */
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->set_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
NULL));
|
||||
plugin->set_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
NULL));
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->get_extension_config (plugin->cls,
|
||||
"fnord",
|
||||
&config));
|
||||
plugin->get_extension_manifest (plugin->cls,
|
||||
"fnord",
|
||||
&manifest));
|
||||
|
||||
FAILIF (NULL != config);
|
||||
FAILIF (NULL != manifest);
|
||||
|
||||
return GNUNET_OK;
|
||||
drop:
|
||||
@ -1272,7 +1272,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 ==
|
||||
@ -1500,7 +1500,7 @@ run (void *cls)
|
||||
&deposit,
|
||||
known_coin_id,
|
||||
&h_payto,
|
||||
false,
|
||||
NULL, /* no policy_details_serial_id */
|
||||
&deposit_timestamp,
|
||||
&balance_ok,
|
||||
&in_conflict));
|
||||
|
@ -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)
|
||||
|
||||
|
33
src/extensions/age_restriction/Makefile.am
Normal file
33
src/extensions/age_restriction/Makefile.am
Normal 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)
|
258
src/extensions/age_restriction/age_restriction.c
Normal file
258
src/extensions/age_restriction/age_restriction.c
Normal 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 */
|
73
src/extensions/age_restriction_helper.c
Normal file
73
src/extensions/age_restriction_helper.c
Normal 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 */
|
@ -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 */
|
@ -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,22 +295,23 @@ TALER_extensions_is_json_config (
|
||||
|
||||
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_extensions_load_json_config (
|
||||
TALER_extensions_load_manifests (
|
||||
json_t *extensions)
|
||||
{
|
||||
const char*name;
|
||||
json_t *blob;
|
||||
json_t *manifest;
|
||||
|
||||
GNUNET_assert (NULL != extensions);
|
||||
GNUNET_assert (json_is_object (extensions));
|
||||
|
||||
json_object_foreach (extensions, name, blob)
|
||||
json_object_foreach (extensions, name, manifest)
|
||||
{
|
||||
int critical;
|
||||
const char *version;
|
||||
json_t *config;
|
||||
const struct TALER_Extension *extension =
|
||||
TALER_extensions_get_by_name (name);
|
||||
struct TALER_Extension *extension = (struct
|
||||
TALER_Extension *)
|
||||
TALER_extensions_get_by_name (name);
|
||||
|
||||
if (NULL == extension)
|
||||
{
|
||||
@ -291,45 +322,109 @@ TALER_extensions_load_json_config (
|
||||
|
||||
/* load and verify criticality, version, etc. */
|
||||
if (GNUNET_OK !=
|
||||
TALER_extensions_is_json_config (
|
||||
blob, &critical, &version, &config))
|
||||
TALER_extensions_parse_manifest (
|
||||
manifest, &critical, &version, &config))
|
||||
return GNUNET_SYSERR;
|
||||
|
||||
if (critical != extension->critical
|
||||
|| 0 != strcmp (version, extension->version) // TODO: libtool compare?
|
||||
|| NULL == config
|
||||
|| GNUNET_OK != extension->test_json_config (config))
|
||||
|| GNUNET_OK != extension->load_config (NULL, config))
|
||||
return GNUNET_SYSERR;
|
||||
|
||||
/* This _should_ work now */
|
||||
if (GNUNET_OK !=
|
||||
extension->load_json_config ((struct TALER_Extension *) extension,
|
||||
config))
|
||||
extension->load_config (extension, config))
|
||||
return GNUNET_SYSERR;
|
||||
|
||||
extension->enabled = true;
|
||||
}
|
||||
|
||||
/* make sure to disable all extensions that weren't mentioned in the json */
|
||||
for (const struct TALER_Extension *it = TALER_extensions_get_head ();
|
||||
for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
|
||||
NULL != it;
|
||||
it = it->next)
|
||||
{
|
||||
if (NULL == json_object_get (extensions, it->name))
|
||||
it->disable ((struct TALER_Extension *) it);
|
||||
if (NULL == json_object_get (extensions, it->extension->name))
|
||||
it->extension->disable ((struct TALER_Extension *) it);
|
||||
}
|
||||
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TALER_extensions_age_restriction_is_enabled ()
|
||||
{
|
||||
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 ());
|
||||
}
|
||||
|
||||
|
||||
|
34
src/extensions/policy_brandt_vickrey_auction/Makefile.am
Normal file
34
src/extensions/policy_brandt_vickrey_auction/Makefile.am
Normal file
@ -0,0 +1,34 @@
|
||||
# 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
|
||||
|
||||
|
||||
# Auction of Brandt type as an extension library
|
||||
|
||||
plugindir = $(libdir)/taler
|
||||
|
||||
plugin_LTLIBRARIES = \
|
||||
libtaler_extension_policy_brandt_vickrey_auction.la
|
||||
|
||||
libtaler_extension_policy_brandt_vickrey_auction_la_LDFLAGS = \
|
||||
-version-info 0:0:0 \
|
||||
-no-undefined
|
||||
|
||||
libtaler_extension_policy_brandt_vickrey_auction_la_SOURCES = \
|
||||
policy_brandt_vickrey_auction.c
|
||||
|
||||
libtaler_extension_policy_brandt_vickrey_auction_la_LIBADD = \
|
||||
$(top_builddir)/src/json/libtalerjson.la \
|
||||
$(top_builddir)/src/util/libtalerutil.la \
|
||||
-lgnunetjson \
|
||||
-lgnunetutil \
|
||||
-ljansson \
|
||||
$(XLIB)
|
@ -0,0 +1,929 @@
|
||||
/*
|
||||
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 policy_brandt_vickrey_auction.c
|
||||
* @brief Extension for replay of auctions of type Brandt
|
||||
* @author Özgür Kesim
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include "taler_util.h"
|
||||
#include "taler_extensions.h"
|
||||
#include "../../exchange/taler-exchange-httpd.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "stdint.h"
|
||||
#include <microhttpd.h>
|
||||
|
||||
#define POLICY_AUCTION "policy_brandt_vickrey_auction"
|
||||
#define LOG_PREFIX "[policy_brandt_vickrey_auction] "
|
||||
#define MAX_RESULT_SIZE 10 * 1024
|
||||
|
||||
/* (public) configuration of this extension */
|
||||
/* FIXME: these fields need to be set in the init handler */
|
||||
static struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig BV_config = {
|
||||
.max_bidders = 10,
|
||||
.max_prices = 10,
|
||||
.auction_fee = {
|
||||
.value = 0,
|
||||
.fraction = 0,
|
||||
.currency = {'E', 'U', 'R', 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
};
|
||||
|
||||
/* Path to the replay program. */
|
||||
static char *replay_program;
|
||||
|
||||
|
||||
/* supported currency */
|
||||
static char *currency;
|
||||
|
||||
/* This is basically BRANDT_Result with an extra string field */
|
||||
struct result
|
||||
{
|
||||
uint16_t bidder;
|
||||
uint16_t price_idx;
|
||||
const char *price;
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief Transcript information
|
||||
*
|
||||
*/
|
||||
struct transcript
|
||||
{
|
||||
/*
|
||||
* The first couple of fields are from a JSON transcript
|
||||
*/
|
||||
|
||||
/* Public key of seller */
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey seller_pub;
|
||||
|
||||
/* Payto URL */
|
||||
const char *payto;
|
||||
|
||||
/* Number of bidders */
|
||||
uint16_t n;
|
||||
|
||||
/* (n-1) public keys of bidders */
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub;
|
||||
|
||||
/* Hash of the auction */
|
||||
struct GNUNET_HashCode h_auction;
|
||||
|
||||
/* (n-1) calculated hash_codes */
|
||||
struct GNUNET_HashCode *hash_codes;
|
||||
|
||||
|
||||
/* Type of auction, see libbrandt */
|
||||
uint16_t m;
|
||||
|
||||
/* Auction public outcome? */
|
||||
bool public;
|
||||
|
||||
/* Start date of the auction */
|
||||
struct GNUNET_TIME_Timestamp time_start;
|
||||
|
||||
/* End date of the auction */
|
||||
struct GNUNET_TIME_Relative time_round;
|
||||
|
||||
/* Number of prices */
|
||||
uint16_t k;
|
||||
|
||||
/* Prices, must be length k */
|
||||
struct TALER_Amount *prices;
|
||||
|
||||
/* Expected winner(s), maybe NULL */
|
||||
struct result *expected;
|
||||
size_t expected_len;
|
||||
|
||||
/*
|
||||
* These are the results from the replay via the external program.
|
||||
*/
|
||||
struct result *results;
|
||||
size_t results_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief returns an JSON with the error
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
json_error (json_t **output,
|
||||
char *error, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int n = 0;
|
||||
char buf[4096];
|
||||
GNUNET_assert (error);
|
||||
|
||||
va_start (ap, error);
|
||||
n = vsprintf (buf, error, ap);
|
||||
va_end (ap);
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
LOG_PREFIX "got error: %s\n",
|
||||
n < 0 ? error: buf);
|
||||
|
||||
*output = json_pack ("{s:s}",
|
||||
"error",
|
||||
n < 0 ? error : buf);
|
||||
GNUNET_assert (*output);
|
||||
|
||||
return GNUNET_SYSERR;
|
||||
};
|
||||
|
||||
|
||||
/* Create serial as H(bidder_pub, h_auction) */
|
||||
static void
|
||||
calculate_hashcode (
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey *pub,
|
||||
struct GNUNET_HashCode *hc,
|
||||
struct GNUNET_HashCode *serial)
|
||||
{
|
||||
struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start ();
|
||||
GNUNET_CRYPTO_hash_context_read (ctx,
|
||||
pub,
|
||||
sizeof(*pub));
|
||||
GNUNET_CRYPTO_hash_context_read (ctx,
|
||||
hc,
|
||||
sizeof(*hc));
|
||||
GNUNET_CRYPTO_hash_context_finish (ctx,
|
||||
serial);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief Parses a given json as transcript.
|
||||
*
|
||||
* @param[in] jtr JSON input
|
||||
* @param[out] tr Parsed transcript data
|
||||
* @param[out] jerror JSON output for errors
|
||||
* @return GNUNET_OK on succes
|
||||
*
|
||||
* FIXME:
|
||||
* - parse and verify signatures
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
parse_transcript (const json_t *jtr,
|
||||
struct transcript *tr,
|
||||
json_t **jerror)
|
||||
{
|
||||
json_t *auc;
|
||||
|
||||
// FIXME: struct GNUNET_CRYPTO_EddsaSignature sig;
|
||||
|
||||
GNUNET_assert (jtr);
|
||||
GNUNET_assert (tr);
|
||||
|
||||
// Parse auction
|
||||
{
|
||||
char *perr;
|
||||
unsigned int eline;
|
||||
struct GNUNET_JSON_Specification au_spec[] = {
|
||||
GNUNET_JSON_spec_bool ("outcome_public", &tr->public),
|
||||
GNUNET_JSON_spec_uint16 ("auction_type", &tr->m),
|
||||
GNUNET_JSON_spec_fixed_auto ("pubkey", &tr->seller_pub),
|
||||
GNUNET_JSON_spec_timestamp ("time_start", &tr->time_start),
|
||||
GNUNET_JSON_spec_relative_time ("time_round", &tr->time_round),
|
||||
GNUNET_JSON_spec_string ("payto_uri", &tr->payto),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
auc = json_object_get (jtr, "auction");
|
||||
if (NULL == auc)
|
||||
return json_error (jerror,
|
||||
"no auction found in transcript");
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (auc,
|
||||
au_spec,
|
||||
(const char **) &perr,
|
||||
&eline))
|
||||
return json_error (jerror,
|
||||
perr);
|
||||
|
||||
{
|
||||
const char *auc_js = json_dumps (auc, JSON_COMPACT);
|
||||
GNUNET_CRYPTO_hash (auc_js,
|
||||
strlen (auc_js),
|
||||
&tr->h_auction);
|
||||
}
|
||||
|
||||
// Prices...
|
||||
{
|
||||
size_t idx;
|
||||
json_t *val;
|
||||
json_t *prices;
|
||||
|
||||
prices = json_object_get (auc, "prices");
|
||||
if (! json_is_array (prices))
|
||||
return json_error (jerror,
|
||||
"no prices found");
|
||||
|
||||
tr->k = json_array_size (prices);
|
||||
|
||||
tr->prices = GNUNET_new_array (tr->k, struct TALER_Amount);
|
||||
json_array_foreach (prices, idx, val)
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_amount (NULL,
|
||||
currency,
|
||||
&(tr->prices[idx])),
|
||||
GNUNET_JSON_spec_end (),
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (val,
|
||||
spec,
|
||||
NULL,
|
||||
NULL))
|
||||
return json_error (jerror,
|
||||
"price no. %ld couldn't be parsed",
|
||||
idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bidders
|
||||
{
|
||||
size_t idx;
|
||||
json_t *val;
|
||||
json_t *bidders;
|
||||
|
||||
bidders = json_object_get (jtr, "bidders");
|
||||
if (! bidders || ! json_is_array (bidders))
|
||||
return json_error (jerror,
|
||||
"no bidders found");
|
||||
|
||||
tr->n = json_array_size (bidders);
|
||||
|
||||
tr->bidder_pub = GNUNET_new_array (
|
||||
tr->n,
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey);
|
||||
|
||||
tr->hash_codes = GNUNET_new_array (
|
||||
tr->n,
|
||||
struct GNUNET_HashCode);
|
||||
|
||||
json_array_foreach (bidders, idx, val)
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto (NULL,
|
||||
&tr->bidder_pub[idx]),
|
||||
GNUNET_JSON_spec_end (),
|
||||
};
|
||||
|
||||
/* FIXME: cleanup */
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (val,
|
||||
spec,
|
||||
NULL,
|
||||
NULL))
|
||||
return json_error (jerror,
|
||||
"bidder no %ld public key couldn't be parsed",
|
||||
idx + 1);
|
||||
|
||||
calculate_hashcode (&tr->bidder_pub[idx],
|
||||
&tr->h_auction,
|
||||
&tr->hash_codes[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// Messages
|
||||
{
|
||||
size_t nm;
|
||||
json_t *messages = json_object_get (jtr, "transcript");
|
||||
|
||||
if (! json_is_array (messages))
|
||||
return json_error (jerror,
|
||||
"no messages found");
|
||||
|
||||
|
||||
nm = json_array_size (messages);
|
||||
|
||||
if (nm != (4 * tr->n))
|
||||
return json_error (jerror,
|
||||
"not the right no. of messages found");
|
||||
|
||||
/* FIXME: parse and evaluate signatures */
|
||||
}
|
||||
|
||||
// Winners
|
||||
{
|
||||
size_t idx;
|
||||
json_t *val;
|
||||
json_t *winners = json_object_get (jtr, "winners");
|
||||
|
||||
if (! json_is_array (winners))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
LOG_PREFIX "winners not provided, continuing without\n");
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
tr->expected_len = json_array_size (winners);
|
||||
tr->expected = GNUNET_new_array (tr->expected_len,
|
||||
struct result);
|
||||
|
||||
json_array_foreach (winners, idx, val) {
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_uint16 ("bidder",
|
||||
&(tr->expected[idx].bidder)),
|
||||
GNUNET_JSON_spec_uint16 ("price_idx",
|
||||
&(tr->expected[idx].price_idx)),
|
||||
GNUNET_JSON_spec_string ("price",
|
||||
&(tr->expected[idx].price)),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (val,
|
||||
spec,
|
||||
NULL,
|
||||
NULL))
|
||||
return json_error (jerror,
|
||||
"couldn't parse winner no. %ld",
|
||||
idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: parse and evalue sig of seller
|
||||
|
||||
// FIXME: check for max values
|
||||
|
||||
DONE:
|
||||
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief replay an auction using the external program
|
||||
*
|
||||
* @param[in] root The original JSON transcript
|
||||
* @param[in,out] transcript The transcript object parsed so far
|
||||
* @param[in/out] details Array of policy details, provided by the exchange
|
||||
* @param[in] details_count number of elements in @e details
|
||||
* @param[out] result The JSON result from the program
|
||||
* @return GNUNET_OK on success
|
||||
*
|
||||
* FIXME: Make this resumable
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
replay_transcript (const json_t*root,
|
||||
struct transcript *tr,
|
||||
struct TALER_PolicyDetails *details,
|
||||
size_t details_count,
|
||||
json_t **result)
|
||||
{
|
||||
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
|
||||
struct GNUNET_DISK_PipeHandle *pi;
|
||||
struct GNUNET_DISK_PipeHandle *po;
|
||||
const struct GNUNET_DISK_FileHandle *fd;
|
||||
struct GNUNET_OS_Process *proc;
|
||||
|
||||
pi = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE);
|
||||
po = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
|
||||
proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
|
||||
pi, po, NULL,
|
||||
replay_program,
|
||||
replay_program,
|
||||
NULL);
|
||||
if (NULL == proc)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX "couldn't create auction replay program '%s'\n",
|
||||
replay_program);
|
||||
|
||||
return json_error (result, "internal error");
|
||||
}
|
||||
|
||||
// Write original transcript JSON to stdin
|
||||
{
|
||||
ssize_t sz;
|
||||
char *str;
|
||||
size_t str_len;
|
||||
|
||||
|
||||
fd = GNUNET_DISK_pipe_handle (pi, GNUNET_DISK_PIPE_END_WRITE);
|
||||
str = json_dumps (root, JSON_COMPACT);
|
||||
str_len = strlen (str);
|
||||
sz = GNUNET_DISK_file_write (fd,
|
||||
str,
|
||||
str_len);
|
||||
free (str);
|
||||
if (sz != str_len)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
LOG_PREFIX "couldn't write all data to replay_program\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Read output from stdout
|
||||
{
|
||||
ssize_t sz;
|
||||
char buf[MAX_RESULT_SIZE];
|
||||
json_error_t error;
|
||||
json_t *res;
|
||||
|
||||
fd = GNUNET_DISK_pipe_handle (po, GNUNET_DISK_PIPE_END_READ);
|
||||
|
||||
sz = GNUNET_DISK_file_read (fd,
|
||||
buf,
|
||||
sizeof(buf));
|
||||
if (GNUNET_SYSERR == sz)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
LOG_PREFIX "couldn't read data from replay_program\n");
|
||||
return json_error (result, "internal error");
|
||||
}
|
||||
|
||||
buf[sz] = 0;
|
||||
res = json_loads (buf,
|
||||
JSON_DECODE_ANY,
|
||||
&error);
|
||||
|
||||
if (! res)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
LOG_PREFIX
|
||||
"couldn't parse response from replay_program: %s (for '%s')\n",
|
||||
error.text,
|
||||
buf);
|
||||
return json_error (result, error.text);
|
||||
}
|
||||
|
||||
// Handle error case first
|
||||
{
|
||||
json_t *err = json_object_get (res,
|
||||
"error");
|
||||
if (NULL != err)
|
||||
{
|
||||
*result = json_copy (res);
|
||||
json_decref (res);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the result
|
||||
{
|
||||
json_t *winners = json_object_get (res,
|
||||
"winners");
|
||||
if ((NULL == winners) ||
|
||||
(! json_is_array (winners)))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX
|
||||
"replay program didn't return a known result type, instead: '%s'\n",
|
||||
json_dumps (res, JSON_INDENT (2)));
|
||||
return json_error (result, "internal error");
|
||||
}
|
||||
|
||||
// FIXME: check each winner with tr->expected, if applicable
|
||||
|
||||
|
||||
/* First, set all details to default state and values */
|
||||
for (size_t i = 0; i< tr->n; i++)
|
||||
{
|
||||
/* no fees for non-winners */
|
||||
TALER_amount_set_zero (details[i].policy_fee.currency,
|
||||
&details[i].policy_fee);
|
||||
|
||||
/* no transferable amounts (=> accumulated_total becomes refreshable) */
|
||||
TALER_amount_set_zero (details[i].transferable_amount.currency,
|
||||
&details[i].transferable_amount);
|
||||
|
||||
details[i].fulfillment_state = TALER_PolicyFulfillmentSuccess;
|
||||
}
|
||||
|
||||
|
||||
/* Now, fill the winner details */
|
||||
{
|
||||
json_t *w;
|
||||
size_t idx;
|
||||
|
||||
/* Set the outcome of the winners */
|
||||
json_array_foreach (winners, idx, w)
|
||||
{
|
||||
// TODO
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
const char *jerror;
|
||||
uint16_t bidder;
|
||||
uint16_t price_idx;
|
||||
struct TALER_Amount price;
|
||||
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_uint16 ("bidder",
|
||||
&bidder),
|
||||
GNUNET_JSON_spec_uint16 ("price_idx",
|
||||
&price_idx),
|
||||
TALER_JSON_spec_amount ("price",
|
||||
BV_config.auction_fee.currency,
|
||||
&price),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
ret = GNUNET_JSON_parse (w,
|
||||
spec,
|
||||
&jerror,
|
||||
NULL);
|
||||
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX
|
||||
"couldn't parse output of replay program: %s\n",
|
||||
jerror);
|
||||
ret = json_error (result, "internal error (replay)");
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
if (bidder > tr->n - 1)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX
|
||||
"replay program sent a bidder out of range\n");
|
||||
ret = json_error (result, "internal error (bidder)");
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
// Fill the details for this winning bidder.
|
||||
{
|
||||
struct TALER_PolicyDetails *det = NULL;
|
||||
struct GNUNET_HashCode *hc = &tr->hash_codes[idx];
|
||||
|
||||
/* Find the corresponding details */
|
||||
for (size_t i = 0; i < tr->n; i++)
|
||||
{
|
||||
if (GNUNET_CRYPTO_hash_cmp (hc,
|
||||
&details[idx].hash_code))
|
||||
{
|
||||
det = &details[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == det)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX
|
||||
"Couldeln't find calculate hash code %s in details\n",
|
||||
GNUNET_h2s (hc));
|
||||
ret = json_error (result, "internal error (details)");
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: return own result object.
|
||||
// FIXME: sign the result by the exchange
|
||||
*result = json_copy (res);
|
||||
json_decref (res);
|
||||
}
|
||||
}
|
||||
ret = GNUNET_OK;
|
||||
}
|
||||
|
||||
ret = GNUNET_OS_process_wait (proc);
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX
|
||||
"error waiting for auction replay program '%s'\n",
|
||||
replay_program);
|
||||
|
||||
json_object_clear (*result);
|
||||
ret = json_error (result, "internal error");
|
||||
}
|
||||
|
||||
DONE:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief implements the TALER_Extension.disable interface.
|
||||
*
|
||||
* @param ext Pointer to the current extension
|
||||
*/
|
||||
static void
|
||||
auction_disable (
|
||||
struct TALER_Extension *ext)
|
||||
{
|
||||
ext->config = NULL;
|
||||
ext->enabled = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 *
|
||||
auction_manifest (
|
||||
const struct TALER_Extension *ext)
|
||||
{
|
||||
struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig *conf = ext->config;
|
||||
GNUNET_assert (conf);
|
||||
|
||||
json_t *config = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_int64 ("max_bidders", conf->max_bidders),
|
||||
GNUNET_JSON_pack_int64 ("max_prices", conf->max_prices),
|
||||
TALER_JSON_pack_amount ("auction_fee", &conf->auction_fee));
|
||||
GNUNET_assert (config);
|
||||
|
||||
return GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_bool ("critical", ext->critical),
|
||||
GNUNET_JSON_pack_string ("version", ext->version),
|
||||
GNUNET_JSON_pack_object_steal ("config", config));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
auction_load_config (
|
||||
struct TALER_Extension *ext,
|
||||
json_t *jconfig)
|
||||
{
|
||||
/* FIXME: parse configuration */
|
||||
ext->enabled = true;
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief implements the TALER_Extension.policy_get_handler
|
||||
*/
|
||||
static MHD_RESULT
|
||||
auction_policy_get_handler (
|
||||
struct MHD_Connection *connection,
|
||||
const char *const args[])
|
||||
{
|
||||
/* FIXME: return some meta-data about supported version, limits, etc.*/
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
LOG_PREFIX "auction_policy_get_handler not implemented yet\n");
|
||||
|
||||
return TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_NOT_IMPLEMENTED,
|
||||
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
|
||||
"auction_policy_get_handler not implemented yet\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief implements the TALER_Extension.policy_post_handler
|
||||
*
|
||||
* FIXME: make this non-blocking
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
auction_policy_post_handler (
|
||||
const json_t *root,
|
||||
const char *const args[],
|
||||
struct TALER_PolicyDetails *details,
|
||||
size_t details_len,
|
||||
json_t **output)
|
||||
{
|
||||
struct transcript tr = {0};
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
do {
|
||||
ret = parse_transcript (root,
|
||||
&tr,
|
||||
output);
|
||||
|
||||
if (GNUNET_OK != ret)
|
||||
break;
|
||||
|
||||
/* Compare the calculated hash_codes of policies with the provided onces */
|
||||
{
|
||||
if (details_len != tr.n)
|
||||
{
|
||||
ret = json_error (output, "wrong number of bidders");
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i<details_len; i++)
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t j = 0; j<tr.n; j++)
|
||||
{
|
||||
if (GNUNET_CRYPTO_hash_cmp (&details[i].hash_code,
|
||||
&tr.hash_codes[j]))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! found)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"details[%ld].hash_code %s not found in transcript\n",
|
||||
i,
|
||||
GNUNET_h2s (&details[i].hash_code));
|
||||
ret = json_error (output, "internal error (hash_code)");
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = replay_transcript (root,
|
||||
&tr,
|
||||
details,
|
||||
details_len,
|
||||
output);
|
||||
} while(0);
|
||||
|
||||
END:
|
||||
GNUNET_free (tr.prices);
|
||||
GNUNET_free (tr.bidder_pub);
|
||||
GNUNET_free (tr.hash_codes);
|
||||
GNUNET_free (tr.expected);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief implements the TALER_Extensions.create_policy_details interface.
|
||||
*
|
||||
* @param[in] input The policy_details for this handler during deposit
|
||||
* @param[out] details On success will contain the details to the policy
|
||||
* @param[out] error_hint On error, will contain a hint exchange should store
|
||||
* the policy_details in the policy_details table.
|
||||
* @return GNUNET_OK if the request was OK
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
auction_create_policy_details (
|
||||
const json_t *input,
|
||||
struct TALER_PolicyDetails *details,
|
||||
const char **error_hint)
|
||||
{
|
||||
enum GNUNET_GenericReturnValue ret = GNUNET_NO;
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey pub;
|
||||
struct GNUNET_HashCode hc;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
/* We ignore the "type" field as it must have been parsed already upstream
|
||||
* - or this handler wouldn't have been called in first place. */
|
||||
GNUNET_JSON_spec_fixed_auto ("bidder_pub", &pub),
|
||||
TALER_JSON_spec_amount ("commitment",
|
||||
BV_config.auction_fee.currency,
|
||||
&details->commitment),
|
||||
GNUNET_JSON_spec_fixed_auto ("h_auction", &hc),
|
||||
GNUNET_JSON_spec_timestamp ("deadline", &details->deadline),
|
||||
GNUNET_JSON_spec_end (),
|
||||
};
|
||||
|
||||
GNUNET_assert (details);
|
||||
|
||||
error_hint = NULL;
|
||||
|
||||
do {
|
||||
ret = GNUNET_JSON_parse (input,
|
||||
spec,
|
||||
error_hint,
|
||||
NULL);
|
||||
|
||||
if (GNUNET_OK != ret)
|
||||
break;
|
||||
|
||||
/* FIXME: check the deadline */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
LOG_PREFIX "check of deadline %ld not implemented!\n",
|
||||
details->deadline.abs_time.abs_value_us);
|
||||
|
||||
calculate_hashcode (&pub, &hc, &details->hash_code);
|
||||
|
||||
ret = GNUNET_OK;
|
||||
} while(0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* The extension struct for auctions of brandt-style */
|
||||
struct TALER_Extension TE_auction_brandt = {
|
||||
.type = TALER_Extension_PolicyBrandtVickeryAuction,
|
||||
.name = POLICY_AUCTION,
|
||||
.critical = false,
|
||||
.version = "0",
|
||||
.enabled = false, /* disabled per default */
|
||||
.config = &BV_config,
|
||||
.disable = &auction_disable,
|
||||
.load_config = &auction_load_config,
|
||||
.manifest = &auction_manifest,
|
||||
.create_policy_details = &auction_create_policy_details,
|
||||
.policy_get_handler = &auction_policy_get_handler,
|
||||
.policy_post_handler = &auction_policy_post_handler,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ===========================================
|
||||
* Handler for GNUNET_PLUGIN_load and _unload
|
||||
* ===========================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialization function for the extension.
|
||||
* Will be called by GNUNET_PLUGIN_load.
|
||||
*
|
||||
* @param arg Configuration - ptr to GNUNET_CONFIGURATION_Handle
|
||||
* @return Pointer to TE_auction_brandt
|
||||
*/
|
||||
struct TALER_Extension *
|
||||
libtaler_extension_policy_brandt_vickrey_auction_init (void *arg)
|
||||
{
|
||||
const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
|
||||
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_currency (cfg,
|
||||
¤cy))
|
||||
return NULL;
|
||||
|
||||
if (GNUNET_SYSERR ==
|
||||
GNUNET_CONFIGURATION_get_value_string (cfg,
|
||||
TALER_EXTENSION_SECTION_PREFIX
|
||||
POLICY_AUCTION,
|
||||
"REPLAY_PROGRAM",
|
||||
&replay_program))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
TALER_EXTENSION_SECTION_PREFIX POLICY_AUCTION,
|
||||
"REPLAY_PROGRAM");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* check if replay_program is actually an executable */
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (0 != stat (replay_program, &sb))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX "replay_program '%s' not found\n",
|
||||
replay_program);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( (sb.st_mode & S_IFDIR) ||
|
||||
! (sb.st_mode & S_IXUSR))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
LOG_PREFIX "replay_program '%s' is not an executable\n",
|
||||
replay_program);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
LOG_PREFIX "loading... using replay_program '%s'\n",
|
||||
replay_program);
|
||||
|
||||
/* FIXME: read other config parameters and generate configuration */
|
||||
|
||||
return &TE_auction_brandt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Tear-down function for the extension.
|
||||
* Will be called by GNUNET_PLUGIN_unload.
|
||||
*
|
||||
* @param ignored
|
||||
* @return null
|
||||
*/
|
||||
void *
|
||||
libtaler_extension_policy_brandt_vickrey_auction_done (void *arg)
|
||||
{
|
||||
auction_disable (&TE_auction_brandt);
|
||||
GNUNET_free (replay_program);
|
||||
replay_program = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* end of policy_brandt_vickrey_auction.c */
|
@ -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,
|
||||
|
@ -317,9 +317,9 @@ struct TALER_AUDITORDB_DepositConfirmation
|
||||
struct TALER_PrivateContractHashP h_contract_terms;
|
||||
|
||||
/**
|
||||
* Hash over the extensions for the deposit.
|
||||
* Hash over the policy for the deposit.
|
||||
*/
|
||||
struct TALER_ExtensionContractHashP h_extensions;
|
||||
struct TALER_ExtensionPolicyHashP h_policy;
|
||||
|
||||
/**
|
||||
* Hash over the wiring information of the merchant.
|
||||
|
@ -638,10 +638,10 @@ 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 to a contract that is shared with the
|
||||
* exchange.
|
||||
*/
|
||||
struct TALER_ExtensionContractHashP
|
||||
struct TALER_ExtensionPolicyHashP
|
||||
{
|
||||
/**
|
||||
* Actual hash value.
|
||||
@ -727,10 +727,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.
|
||||
@ -3239,7 +3239,7 @@ TALER_wallet_reserve_attest_request_verify (
|
||||
* @param h_wire hash of the merchant’s account details
|
||||
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
|
||||
* @param h_age_commitment hash over the age commitment, if applicable to the denomination (maybe NULL)
|
||||
* @param h_extensions hash over the extensions
|
||||
* @param h_policy hash over the policy
|
||||
* @param h_denom_pub hash of the coin denomination's public key
|
||||
* @param coin_priv coin’s private key
|
||||
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
|
||||
@ -3254,7 +3254,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,
|
||||
@ -3271,7 +3271,7 @@ TALER_wallet_deposit_sign (
|
||||
* @param h_wire hash of the merchant’s account details
|
||||
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
|
||||
* @param h_age_commitment hash over the age commitment (maybe all zeroes, if not applicable to the denomination)
|
||||
* @param h_extensions hash over the extensions
|
||||
* @param h_policy hash over the policy
|
||||
* @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)
|
||||
@ -3287,7 +3287,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,
|
||||
@ -3692,7 +3692,7 @@ typedef enum TALER_ErrorCode
|
||||
* @param scb function to call to create the signature
|
||||
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
|
||||
* @param h_wire hash of the merchant’s account details
|
||||
* @param h_extensions hash over the extensions, can be NULL
|
||||
* @param h_policy hash over the policy, 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
|
||||
@ -3708,7 +3708,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,
|
||||
@ -3724,7 +3724,7 @@ TALER_exchange_online_deposit_confirmation_sign (
|
||||
*
|
||||
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
|
||||
* @param h_wire hash of the merchant’s account details
|
||||
* @param h_extensions hash over the extensions, can be NULL
|
||||
* @param h_policy hash over the policy, 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
|
||||
@ -3739,7 +3739,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,
|
||||
@ -5242,31 +5242,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_manifests hash of the JSON blob of a configuration of an extension
|
||||
* @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_manifests,
|
||||
const struct TALER_MasterPublicKeyP *master_pub,
|
||||
const struct TALER_MasterSignatureP *master_sig
|
||||
);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <gnunet/gnunet_db_lib.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_signatures.h"
|
||||
#include "taler_extensions_policy.h"
|
||||
|
||||
|
||||
/**
|
||||
@ -221,7 +222,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_FULFILMENTS,
|
||||
TALER_EXCHANGEDB_RT_PURSE_REQUESTS,
|
||||
TALER_EXCHANGEDB_RT_PURSE_REFUNDS,
|
||||
TALER_EXCHANGEDB_RT_PURSE_MERGES,
|
||||
@ -448,8 +450,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
|
||||
@ -521,13 +523,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
|
||||
{
|
||||
@ -871,7 +892,6 @@ struct TALER_EXCHANGEDB_DenominationKeyMetaData
|
||||
struct TALER_AgeMask age_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Signature of a function called with information about the exchange's
|
||||
* denomination keys.
|
||||
@ -1427,12 +1447,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.
|
||||
@ -1474,6 +1488,17 @@ 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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -1572,6 +1597,18 @@ 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?
|
||||
*/
|
||||
@ -3292,6 +3329,39 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
bool *conflict,
|
||||
bool *nonce_reuse);
|
||||
|
||||
/**
|
||||
* Retrieve the details to a policy given by its hash_code
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param hc Hash code that identifies the policy
|
||||
* @param[out] detail retrieved policy details
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*get_policy_details)(
|
||||
void *cls,
|
||||
const struct GNUNET_HashCode *hc,
|
||||
struct TALER_PolicyDetails *detail);
|
||||
|
||||
/**
|
||||
* Persist the policy details that extends a deposit. The particular policy
|
||||
* - referenced by details->hash_code - might already exist in the table, in
|
||||
* which case the call will update the contents of the record with @e details
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param details The parsed `struct TALER_PolicyDetails` according to the responsible policy extension.
|
||||
* @param[out] policy_details_serial_id The ID of the entry in the policy_details table
|
||||
* @param[out] accumulated_total The total amount accumulated in that policy
|
||||
* @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready.
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*persist_policy_details)(
|
||||
void *cls,
|
||||
const struct TALER_PolicyDetails *details,
|
||||
uint64_t *policy_details_serial_id,
|
||||
struct TALER_Amount *accumulated_total,
|
||||
enum TALER_PolicyFulfillmentState *fulfillment_state);
|
||||
|
||||
/**
|
||||
* Perform deposit operation, checking for sufficient balance
|
||||
@ -3301,7 +3371,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
|
||||
@ -3313,7 +3383,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);
|
||||
@ -3341,6 +3411,18 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
bool *zombie_required,
|
||||
bool *balance_ok);
|
||||
|
||||
/**
|
||||
* Add a proof of fulfillment of an policy
|
||||
*
|
||||
* @param cls the plugin-specific state
|
||||
* @param[in,out] fulfillment The proof of fulfillment and serial_ids of the policy_details along with their new state and potential new amounts.
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*add_policy_fulfillment_proof)(
|
||||
void *cls,
|
||||
struct TALER_PolicyFulfillmentTransactionData *fulfillment);
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given @a nonce was properly locked to the given @a old_coin_pub. If so, check if we already
|
||||
@ -5223,23 +5305,23 @@ 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_merchant_refund, ...)
|
||||
*
|
||||
* @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 Manifest of the extension encoded as JSON object, maybe NULL (== disabled extension)
|
||||
* @return transaction status code
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*set_extension_config)(void *cls,
|
||||
const char *extension_name,
|
||||
const char *config);
|
||||
(*set_extension_manifest)(void *cls,
|
||||
const char *extension_name,
|
||||
const char *manifest);
|
||||
|
||||
|
||||
/**
|
||||
* Function called to retrieve the configuration of an extension
|
||||
* (age-restriction, peer2peer, ...)
|
||||
* Function called to retrieve the manifest of an extension
|
||||
* (age_restriction, policy_merchant_refund, ...)
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param extension_name the name of the extension
|
||||
@ -5247,9 +5329,9 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @return transaction status code
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*get_extension_config)(void *cls,
|
||||
const char *extension_name,
|
||||
char **config);
|
||||
(*get_extension_manifest)(void *cls,
|
||||
const char *extension_name,
|
||||
char **manifest);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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,119 +24,253 @@
|
||||
#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
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* @brief simply linked list of extensions
|
||||
*/
|
||||
|
||||
struct TALER_Extensions
|
||||
{
|
||||
struct TALER_Extensions *next;
|
||||
const struct TALER_Extension *extension;
|
||||
};
|
||||
|
||||
/* 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[]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generic functions for extensions
|
||||
*/
|
||||
|
||||
/*
|
||||
* @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
|
||||
|
198
src/include/taler_extensions_policy.h
Normal file
198
src/include/taler_extensions_policy.h
Normal 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
|
@ -767,25 +767,25 @@ TALER_JSON_wire_to_payto (const json_t *wire_s);
|
||||
|
||||
|
||||
/**
|
||||
* Hash @a extensions in deposits.
|
||||
* Hash @a policy in deposits.
|
||||
*
|
||||
* @param extensions contract extensions to hash
|
||||
* @param[out] ech where to write the extension hash
|
||||
* @param policy contract policy 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 *policy,
|
||||
struct TALER_ExtensionPolicyHashP *ech);
|
||||
|
||||
/**
|
||||
* Hash the @a config of an extension, given as JSON
|
||||
*
|
||||
* @param config configuration of the extension
|
||||
* @param manifests manifests of extensions
|
||||
* @param[out] eh where to write the extension 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.
|
||||
|
@ -492,5 +492,64 @@ 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
|
||||
|
@ -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,10 +1037,10 @@ 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,
|
||||
return dump_and_hash (manifests,
|
||||
"taler-extension-configuration",
|
||||
&ech->hash);
|
||||
}
|
||||
|
@ -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 extensions, 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",
|
||||
|
@ -97,9 +97,9 @@ struct TALER_EXCHANGE_BatchDepositHandle
|
||||
struct TALER_MerchantWireHashP h_wire;
|
||||
|
||||
/**
|
||||
* Hash over the extensions, or all zero.
|
||||
* Hash over the policy, 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,
|
||||
@ -493,8 +493,8 @@ TALER_EXCHANGE_batch_deposit (
|
||||
dh->num_cdds = num_cdds;
|
||||
dh->dcd = *dcd;
|
||||
if (NULL != dcd->extension_details)
|
||||
TALER_deposit_extension_hash (dcd->extension_details,
|
||||
&dh->h_extensions);
|
||||
TALER_deposit_policy_hash (dcd->extension_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))
|
||||
|
@ -724,7 +724,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};
|
||||
@ -739,6 +740,10 @@ help_deposit (struct CoinHistoryParseContext *pc,
|
||||
&h_contract_terms),
|
||||
GNUNET_JSON_spec_fixed_auto ("h_wire",
|
||||
&h_wire),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_fixed_auto ("h_policy",
|
||||
&h_policy),
|
||||
&no_h_policy),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
|
||||
&hac),
|
||||
@ -771,7 +776,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,
|
||||
@ -1905,7 +1910,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)
|
||||
|
@ -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 eph 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 *eph,
|
||||
const struct TALER_MerchantWireHashP *h_wire,
|
||||
const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
|
||||
const struct TALER_EXCHANGE_DenomPublicKey *dki);
|
||||
|
@ -97,9 +97,9 @@ struct TALER_EXCHANGE_DepositHandle
|
||||
struct TALER_MerchantWireHashP h_wire;
|
||||
|
||||
/**
|
||||
* Hash over the extensions, or all zero.
|
||||
* Hash over the policy, 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,
|
||||
@ -447,14 +447,14 @@ TALER_EXCHANGE_deposit (
|
||||
dh->cdd = *cdd;
|
||||
dh->dcd = *dcd;
|
||||
if (NULL != dcd->extension_details)
|
||||
TALER_deposit_extension_hash (dcd->extension_details,
|
||||
&dh->h_extensions);
|
||||
TALER_deposit_policy_hash (dcd->extension_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))
|
||||
|
@ -897,21 +897,24 @@ decode_keys_json (const json_t *resp_obj,
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the supported extension(s): age-restriction. */
|
||||
/* Parse the supported extension(s): age_restriction. */
|
||||
/* 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 ()
|
||||
};
|
||||
|
||||
@ -921,22 +924,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 ();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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",
|
||||
@ -280,7 +285,7 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
|
||||
no_hac
|
||||
? NULL
|
||||
: &h_age_commitment,
|
||||
NULL /* FIXME #7270-OEC: h_extensions! */,
|
||||
no_h_policy ? NULL : &h_policy,
|
||||
&h_denom_pub,
|
||||
wallet_timestamp,
|
||||
&merchant_pub,
|
||||
|
2531
src/testing/exec
Normal file
2531
src/testing/exec
Normal file
File diff suppressed because it is too large
Load Diff
3224
src/testing/fnord
Normal file
3224
src/testing/fnord
Normal file
File diff suppressed because it is too large
Load Diff
26187
src/testing/keys-cs.json
Normal file
26187
src/testing/keys-cs.json
Normal file
File diff suppressed because it is too large
Load Diff
2177
src/testing/keys-rsa.json
Normal file
2177
src/testing/keys-rsa.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1315,9 +1315,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"));
|
||||
|
160
src/testing/test_exchange_auction.conf
Normal file
160
src/testing/test_exchange_auction.conf
Normal file
@ -0,0 +1,160 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
|
||||
[PATHS]
|
||||
# Persistent data storage for the testcase
|
||||
TALER_TEST_HOME = test_exchange_api_home/
|
||||
TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/
|
||||
|
||||
[taler-exchange-secmod-rsa]
|
||||
# Reduce from 1 year to speed up test
|
||||
LOOKAHEAD_SIGN = 24 days
|
||||
|
||||
[taler-exchange-secmod-eddsa]
|
||||
# Reduce from 1 year to speed up test
|
||||
LOOKAHEAD_SIGN = 24 days
|
||||
# Reduce from 12 weeks to ensure we have multiple
|
||||
DURATION = 14 days
|
||||
|
||||
[taler]
|
||||
# Currency supported by the exchange (can only be one)
|
||||
CURRENCY = EUR
|
||||
CURRENCY_ROUND_UNIT = EUR:0.01
|
||||
|
||||
[auditor]
|
||||
BASE_URL = "http://localhost:8083/"
|
||||
|
||||
# HTTP port the auditor listens to
|
||||
PORT = 8083
|
||||
|
||||
[exchange]
|
||||
|
||||
TERMS_ETAG = 0
|
||||
PRIVACY_ETAG = 0
|
||||
|
||||
# HTTP port the exchange listens to
|
||||
PORT = 8081
|
||||
|
||||
# Master public key used to sign the exchange's various keys
|
||||
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
|
||||
# How to access our database
|
||||
DB = postgres
|
||||
|
||||
# Base URL of the exchange. Must be set to a URL where the
|
||||
# exchange (or the twister) is actually listening.
|
||||
BASE_URL = "http://localhost:8081/"
|
||||
|
||||
# How big is an individual shard to be processed
|
||||
# by taler-exchange-expire (in time). It may take
|
||||
# this much time for an expired purse to be really
|
||||
# cleaned up and the coins refunded.
|
||||
EXPIRE_SHARD_SIZE = 300 ms
|
||||
|
||||
EXPIRE_IDLE_SLEEP_INTERVAL = 1 s
|
||||
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG = "postgres:///talercheck"
|
||||
|
||||
[auditordb-postgres]
|
||||
CONFIG = "postgres:///talercheck"
|
||||
|
||||
# Sections starting with "exchange-account-" configure the bank accounts
|
||||
# of the exchange. The "URL" specifies the account in
|
||||
# payto://-format.
|
||||
[exchange-account-1]
|
||||
# What is the URL of our account?
|
||||
PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42"
|
||||
# ENABLE_CREDIT = YES
|
||||
|
||||
[exchange-accountcredentials-1]
|
||||
WIRE_GATEWAY_URL = "http://localhost:9081/42/"
|
||||
|
||||
[exchange-account-2]
|
||||
# What is the bank account (with the "Taler Bank" demo system)?
|
||||
PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2"
|
||||
ENABLE_DEBIT = YES
|
||||
ENABLE_CREDIT = YES
|
||||
|
||||
[exchange-accountcredentials-2]
|
||||
WIRE_GATEWAY_AUTH_METHOD = basic
|
||||
USERNAME = Exchange
|
||||
PASSWORD = x
|
||||
WIRE_GATEWAY_URL = "http://localhost:9081/2/"
|
||||
|
||||
[bank]
|
||||
HTTP_PORT = 9081
|
||||
|
||||
# Enabled extensions
|
||||
#[exchange-extension-age_restriction]
|
||||
#ENABLED = YES
|
||||
# default age groups:
|
||||
#AGE_GROUPS = "8:10:12:14:16:18:21"
|
||||
|
||||
# Enable policy of type auction for deposits.
|
||||
[exchange-extension-policy_brandt_vickrey_auction]
|
||||
ENABLED = YES
|
||||
REPLAY_PROGRAM = "/usr/local/bin/brandt_vickrey_auction-replay"
|
||||
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 1024
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 1024
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 1024
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 1024
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 1024
|
@ -417,9 +417,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"));
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
@ -396,6 +401,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename,
|
||||
TALER_TESTING_auditor_db_reset (config_filename))
|
||||
return GNUNET_NO;
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_parse_and_run (config_filename,
|
||||
&sign_keys_for_exchange,
|
||||
@ -709,6 +715,10 @@ TALER_TESTING_setup_with_exchange_cfg (
|
||||
char *base_url;
|
||||
int result;
|
||||
|
||||
/* Load extensions */
|
||||
if (GNUNET_OK != TALER_extensions_init (cfg))
|
||||
return GNUNET_SYSERR;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (cfg,
|
||||
"exchange",
|
||||
|
@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \
|
||||
|
||||
libtalerutil_la_LIBADD = \
|
||||
-lgnunetutil \
|
||||
-lgnunetjson \
|
||||
-lsodium \
|
||||
-ljansson \
|
||||
$(LIBGCRYPT_LIBS) \
|
||||
|
@ -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 */
|
||||
|
@ -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 policy extension options 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 !=
|
||||
|
@ -942,7 +942,7 @@ GNUNET_NETWORK_STRUCT_BEGIN
|
||||
* @brief Signature made by the exchange offline key over the
|
||||
* configuration of an extension.
|
||||
*/
|
||||
struct TALER_MasterExtensionConfigurationPS
|
||||
struct TALER_MasterExtensionManifestsPS
|
||||
{
|
||||
/**
|
||||
* Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed
|
||||
@ -953,22 +953,22 @@ struct TALER_MasterExtensionConfigurationPS
|
||||
/**
|
||||
* Hash of the JSON object that represents the configuration of an extension.
|
||||
*/
|
||||
struct TALER_ExtensionConfigHashP h_config GNUNET_PACKED;
|
||||
struct TALER_ExtensionManifestsHashP h_manifests 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_manifests,
|
||||
const struct TALER_MasterPrivateKeyP *master_priv,
|
||||
struct TALER_MasterSignatureP *master_sig)
|
||||
{
|
||||
struct TALER_MasterExtensionConfigurationPS ec = {
|
||||
struct TALER_MasterExtensionManifestsPS ec = {
|
||||
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
|
||||
.purpose.size = htonl (sizeof(ec)),
|
||||
.h_config = *h_config
|
||||
.h_manifests = *h_manifests
|
||||
};
|
||||
GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
|
||||
&ec,
|
||||
@ -977,16 +977,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_manifests,
|
||||
const struct TALER_MasterPublicKeyP *master_pub,
|
||||
const struct TALER_MasterSignatureP *master_sig
|
||||
)
|
||||
{
|
||||
struct TALER_MasterExtensionConfigurationPS ec = {
|
||||
struct TALER_MasterExtensionManifestsPS ec = {
|
||||
.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
|
||||
.purpose.size = htonl (sizeof(ec)),
|
||||
.h_config = *h_config
|
||||
.h_manifests = *h_manifests
|
||||
};
|
||||
|
||||
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,
|
||||
|
@ -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 policy 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,
|
||||
|
Loading…
Reference in New Issue
Block a user