Compare commits

...

32 Commits

Author SHA1 Message Date
949a91c38e
accumulate amounts from coins in batch-deposit for policy 2022-10-14 20:23:59 +02:00
63b19fad0c
simplify datastructures 2022-10-14 20:10:06 +02:00
29e5cd0ef5
fix errors 2022-10-14 20:09:39 +02:00
15e8697ece
-added missing files to last commit 2022-10-14 19:00:57 +02:00
054e157af8
WIP: policy_details handling continued
- policy details generated on deposit/batch-deposit requests
- insert or update of policy details in the DB
- accumulation of amounts of multiple deposits for the same
  policy_details
2022-10-14 18:56:59 +02:00
956e3c3065
WIP: policy handling advancements
- introduction of policy states.
- introduction of default state on timeout.
- introduction of TALER_PolicyFulfilmentOutcome
- parsing for policy_details now also provides default state on timeout.
- introduction of check_serial as preflight func before heavy operations
  in policy post handler
- brandt auction now returns outcomes.
2022-10-09 22:11:09 +02:00
18d8dcd1f9
-fix type 2022-10-08 17:27:00 +02:00
4154a0c466
-logic fixed 2022-10-08 17:20:59 +02:00
4ba07b54e4
WIP: policy_*_fulfiment added and API changes
- policy_fulfilment table defined, handlers added
- policy_details_fulfilment table defined, handlers added
- TALER_extensions_serial_from_policy_details implemened
2022-10-08 17:13:57 +02:00
d3c509fcd9
WIP: Added policy extension handling during deposit 2022-10-07 09:59:39 +02:00
52106eea42
refactor: extensions_ -> policy_; added policy_fulfilment 2022-10-06 23:17:46 +02:00
dd60d9b984
Merge branch 'master' into auction_brandt 2022-10-06 21:12:17 +02:00
b447506342
refinements of policy extension APIs 2022-10-06 21:11:47 +02:00
086c4a243f
mark future policy extensions 2022-10-06 21:09:59 +02:00
cf21ee2647
-rm todo.md 2022-10-06 21:09:33 +02:00
1f6c8d0c33
refactor h_extensions -> h_policy, and structs accordingly 2022-10-05 19:26:04 +02:00
bcbd909892
-debug msg removed; typos 2022-10-05 16:29:23 +02:00
fc83650115
refactor extensions: config -> manifest 2022-10-05 16:07:22 +02:00
2524dfc8d3
refactor extensions: config -> manifest 2022-10-05 16:07:09 +02:00
477d009cb0
Merge branch 'master' into auction_brandt 2022-10-04 21:42:21 +02:00
0a089db4ef
rename auction_brandt -> policy_auction 2022-10-04 21:31:19 +02:00
0ad6a8cbfb
Merge branch 'master' into auction_brandt 2022-10-04 20:06:49 +02:00
77266e6c93
Merge branch 'master' into auction_brandt 2022-10-04 11:35:21 +02:00
3fdf88f612
-fix some vars 2022-10-04 11:34:46 +02:00
3ec07d62c3
improved error handling 2022-10-04 11:33:21 +02:00
424bbddaa3
correct http reply 2022-10-04 11:32:10 +02:00
64ebcadabb
added targets for age_restriciton and auction_brandt 2022-10-04 11:31:17 +02:00
e1bf3661ec
-fix unsused variable 2022-10-03 11:21:07 +02:00
12736abcf7
WiP: parsing and replay of auction transcripts
POST handler of auction_brandt extension now parses JSON transcript and
passes JSON via stdin to external program.  Reads result via stdout.

TODOs:
- check signatures in transcript
- parse JSON output of replay progam
- add configuration to to auction_brandt:
	- types of auctions
	- max. concurrent users
	- max. number of prices
	- maximum price
	- fees
- add timeout handler for replay program execution
- (maybe) sign output of exchange by master-key
- (maybe) add GET handler with information re: auction_brandt?
2022-10-03 08:15:07 +02:00
b2d68d4b2d
-rm debug info; typo 2022-10-02 23:07:40 +02:00
72cbf66395
WiP: added auction_brandt as extension
- added an extension to handle auctions via libbrandt
  - /extensions/auction_brandt GET and POST handler defined
  - initial parsing of transcript.json implemented, yet WiP

- multiple cleanups and changes to extension handling
2022-10-02 22:51:54 +02:00
04c7e0bb33
Refactor extensions
- Extensions are now compiled as shared libraries and loaded at runtime
  according to the TALER configuration.

- So far, only age restriction is loaded as extension.

- Groundwork for extension auction_brandt started.
2022-10-02 18:05:58 +02:00
69 changed files with 37941 additions and 1198 deletions

View File

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

View File

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

View File

@ -227,7 +227,7 @@ verify_and_execute_deposit_confirmation (
TALER_exchange_online_deposit_confirmation_verify (
&dc->h_contract_terms,
&dc->h_wire,
NULL /* h_extensions! */,
&dc->h_policy,
dc->exchange_timestamp,
dc->wire_deadline,
dc->refund_deadline,
@ -276,8 +276,8 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&dc.h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_extensions",
&dc.h_extensions),
GNUNET_JSON_spec_fixed_auto ("h_policy",
&dc.h_policy),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&dc.h_wire),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -137,6 +137,12 @@ static struct GNUNET_CURL_RescheduleContext *rc;
*/
static const struct GNUNET_CONFIGURATION_Handle *kcfg;
/**
* Age restriction configuration
*/
static bool ar_enabled = false;
static struct TALER_AgeRestrictionConfig ar_config = {0};
/**
* Return value from main().
*/
@ -163,11 +169,6 @@ static char *currency;
*/
static char *CFG_exchange_url;
/**
* If age restriction is enabled, the age mask to be used
*/
static struct TALER_AgeMask age_mask = {0};
/**
* A subcommand supported by this program.
*/
@ -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,
&currency))
@ -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 */

View File

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

View File

@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/
extern char *TEH_currency;
/*
* Age restriction extension state
*/
extern bool TEH_age_restriction_enabled;
/**
* Our (externally visible) base URL.
*/
@ -221,6 +216,7 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
* Signature of the offline master key of all enabled extensions' configuration
*/
extern struct TALER_MasterSignatureP TEH_extensions_sig;
extern bool TEH_extensions_signed;
/**
* @brief Struct describing an URL and the handler for it.
@ -366,4 +362,8 @@ struct TEH_RequestHandler
};
/* Age restriction configuration */
extern bool TEH_age_restriction_enabled;
extern struct TALER_AgeRestrictionConfig TEH_age_restriction_config;
#endif

View File

@ -87,15 +87,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);

View File

@ -21,6 +21,7 @@
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
* @author Özgür Kesim
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@ -47,7 +48,7 @@
* @param connection connection to the client
* @param coin_pub public key of the coin
* @param h_wire hash of wire details
* @param h_extensions hash of applicable extensions
* @param h_policy hash of applicable 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,

View File

@ -14,7 +14,7 @@
*/
/**
* @file taler-exchange-httpd_extensions.c
* @brief Handle extensions (age-restriction, peer2peer)
* @brief Handle extensions (age-restriction, policy extensions)
* @author Özgür Kesim
*/
#include "platform.h"
@ -77,65 +77,84 @@ extension_update_event_cb (void *cls,
return;
}
// Get the config from the database as string
// Get the manifest from the database as string
{
char *config_str = NULL;
char *manifest_str = NULL;
enum GNUNET_DB_QueryStatus qs;
json_error_t err;
json_t *config;
json_t *manifest_js;
enum GNUNET_GenericReturnValue ret;
qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
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 */

View File

@ -40,4 +40,19 @@ TEH_extensions_init (void);
void
TEH_extensions_done (void);
/**
* Handle POST "/extensions/..." requests.
*
* @param rc request context
* @param root uploaded JSON data
* @param args array of additional options
* @return MHD result code
*/
MHD_RESULT
TEH_extensions_post_handler (
struct TEH_RequestContext *rc,
const json_t *root,
const char *const args[]);
#endif

View File

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

View File

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

View File

@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest
TEH_MT_REQUEST_IDEMPOTENT_MELT = 10,
TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11,
TEH_MT_REQUEST_BATCH_DEPOSIT = 12,
TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */
TEH_MT_REQUEST_POLICY_FULFILMENT = 13,
TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
};
/**

View File

@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations (
bool failed = true;
/* Has been checked in handle_refreshes_reveal_json() */
GNUNET_assert (ng ==
TALER_extensions_age_restriction_num_groups ());
GNUNET_assert (ng == TEH_age_restriction_config.num_groups);
rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment);
oac = rctx->old_age_commitment;
@ -931,7 +930,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
/* Sanity check of age commitment: If it was provided, it _must_ be an array
* of the size the # of age groups */
if (NULL != old_age_commitment_json
&& TALER_extensions_age_restriction_num_groups () !=
&& TEH_age_restriction_config.num_groups !=
json_array_size (old_age_commitment_json))
{
GNUNET_break_op (0);

View File

@ -76,7 +76,7 @@ TEH_RESPONSE_compile_transaction_history (
&h_wire,
&deposit->h_contract_terms,
&deposit->h_age_commitment,
NULL /* h_extensions! */,
(deposit->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 (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -510,8 +510,8 @@ CREATE OR REPLACE FUNCTION exchange_do_deposit(
IN in_coin_pub BYTEA,
IN in_coin_sig BYTEA,
IN in_shard INT8,
IN in_extension_blocked BOOLEAN,
IN in_extension_details VARCHAR,
IN in_policy_blocked BOOLEAN,
IN in_policy_details_serial_id INT8,
OUT out_exchange_timestamp INT8,
OUT out_balance_ok BOOLEAN,
OUT out_conflict BOOLEAN)
@ -519,26 +519,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;

View File

@ -112,53 +112,53 @@ mark_prepare_cb (void *cls,
* Simple check that config retrieval and setting for extensions work
*/
static enum GNUNET_GenericReturnValue
test_extension_config (void)
test_extension_manifest (void)
{
char *config;
char *manifest;
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_extension_config (plugin->cls,
"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));

View File

@ -11,7 +11,7 @@ if USE_COVERAGE
endif
# Libraries
# Basic extension handling library
lib_LTLIBRARIES = \
libtalerextensions.la
@ -22,7 +22,7 @@ libtalerextensions_la_LDFLAGS = \
libtalerextensions_la_SOURCES = \
extensions.c \
extension_age_restriction.c
age_restriction_helper.c
libtalerextensions_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@ -31,3 +31,4 @@ libtalerextensions_la_LIBADD = \
-lgnunetutil \
-ljansson \
$(XLIB)

View File

@ -0,0 +1,33 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = \
-I$(top_srcdir)/src/include \
$(LIBGCRYPT_CFLAGS) \
$(POSTGRESQL_CPPFLAGS)
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
# Age restriction as extension library
plugindir = $(libdir)/taler
plugin_LTLIBRARIES = \
libtaler_extension_age_restriction.la
libtaler_extension_age_restriction_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtaler_extension_age_restriction_la_SOURCES = \
age_restriction.c
libtaler_extension_age_restriction_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetjson \
-lgnunetutil \
-ljansson \
$(XLIB)

View File

@ -0,0 +1,258 @@
/*
This file is part of TALER
Copyright (C) 2021-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file age_restriction.c
* @brief Utility functions regarding age restriction
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_extensions.h"
#include "stdint.h"
/* ==================================================
*
* Age Restriction TALER_Extension implementation
*
* ==================================================
*/
/**
* @brief local configuration
*/
static struct TALER_AgeRestrictionConfig AR_config = {0};
/**
* @brief implements the TALER_Extension.disable interface.
*
* @param ext Pointer to the current extension
*/
static void
age_restriction_disable (
struct TALER_Extension *ext)
{
if (NULL == ext)
return;
ext->enabled = false;
ext->config = NULL;
AR_config.mask.bits = 0;
AR_config.num_groups = 0;
}
/**
* @brief implements the TALER_Extension.load_config interface.
*
* @param ext if NULL, only tests the configuration
* @param jconfig the configuration as json
*/
static enum GNUNET_GenericReturnValue
age_restriction_load_config (
struct TALER_Extension *ext,
json_t *jconfig)
{
struct TALER_AgeMask mask = {0};
enum GNUNET_GenericReturnValue ret;
ret = TALER_JSON_parse_age_groups (jconfig, &mask);
if (GNUNET_OK != ret)
return ret;
/* only testing the parser */
if (ext == NULL)
return GNUNET_OK;
if (TALER_Extension_AgeRestriction != ext->type)
return GNUNET_SYSERR;
if (mask.bits > 0)
{
/* if the mask is not zero, the first bit MUST be set */
if (0 == (mask.bits & 1))
return GNUNET_SYSERR;
AR_config.mask.bits = mask.bits;
AR_config.num_groups = __builtin_popcount (mask.bits) - 1;
}
ext->config = &AR_config;
ext->enabled = true;
json_decref (jconfig);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"loaded new age restriction config with age groups: %s\n",
TALER_age_mask_to_string (&mask));
return GNUNET_OK;
}
/**
* @brief implements the TALER_Extension.manifest interface.
*
* @param ext if NULL, only tests the configuration
* @return configuration as json_t* object, maybe NULL
*/
static json_t *
age_restriction_manifest (
const struct TALER_Extension *ext)
{
char *mask_str;
json_t *conf;
GNUNET_assert (NULL != ext);
if (NULL == ext->config)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"age restriction not configured");
return json_null ();
}
mask_str = TALER_age_mask_to_string (&AR_config.mask);
conf = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("age_groups", mask_str)
);
free (mask_str);
return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical),
GNUNET_JSON_pack_string ("version", ext->version),
GNUNET_JSON_pack_object_steal ("config", conf)
);
}
/* The extension for age restriction */
struct TALER_Extension TE_age_restriction = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.version = "1",
.enabled = false, /* disabled per default */
.config = NULL,
.disable = &age_restriction_disable,
.load_config = &age_restriction_load_config,
.manifest = &age_restriction_manifest,
/* This extension is not a policy extension */
.create_policy_details = NULL,
.policy_get_handler = NULL,
.policy_post_handler = NULL,
};
/**
* @brief implements the init() function for GNUNET_PLUGIN_load
*
* @param arg Pointer to the GNUNET_CONFIGURATION_Handle
* @return pointer to TALER_Extension on success or NULL otherwise.
*/
void *
libtaler_extension_age_restriction_init (void *arg)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
char *groups = NULL;
struct TALER_AgeMask mask = {0};
if ((GNUNET_YES !=
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED"))
||
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_yesno (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED")))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] no section %s found in configuration\n",
TALER_EXTENSION_SECTION_AGE_RESTRICTION);
return NULL;
}
/* Age restriction is enabled, extract age groups */
if ((GNUNET_YES ==
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS"))
&&
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_string (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS",
&groups)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] AGE_GROUPS in %s is not a string\n",
TALER_EXTENSION_SECTION_AGE_RESTRICTION);
return NULL;
}
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
if ((groups != NULL) &&
(GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] couldn't parse age groups: '%s'\n",
groups);
return NULL;
}
AR_config.mask = mask;
AR_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[age restriction] setting age mask to %s with #groups: %d\n",
TALER_age_mask_to_string (&AR_config.mask),
__builtin_popcount (AR_config.mask.bits) - 1);
TE_age_restriction.config = &AR_config;
/* Note: we do now have TE_age_restriction_config set, however the extension
* is not yet enabled! For age restriction to become active, load_config must
* have been called. */
GNUNET_free (groups);
return &TE_age_restriction;
}
/**
* @brief implements the done() function for GNUNET_PLUGIN_load
*
* @param cfg unsued
* @return pointer to TALER_Extension on success or NULL otherwise.
*/
void *
libtaler_extension_age_restriction_done (void *arg)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[age restriction] disabling and unloading");
AR_config.mask.bits = 0;
AR_config.num_groups = 0;
return NULL;
}
/* end of age_restriction.c */

View File

@ -0,0 +1,73 @@
/*
This file is part of TALER
Copyright (C) 2022- Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file age_restriction_helper.c
* @brief Helper functions for age restriction
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
#include "taler_extensions.h"
#include "stdint.h"
const struct TALER_AgeRestrictionConfig *
TALER_extensions_get_age_restriction_config ()
{
const struct TALER_Extension *ext;
ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
if (NULL == ext)
return NULL;
return ext->config;
}
bool
TALER_extensions_is_age_restriction_enabled ()
{
const struct TALER_Extension *ext;
ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
if (NULL == ext)
return false;
return ext->enabled;
}
struct TALER_AgeMask
TALER_extensions_get_age_restriction_mask ()
{
const struct TALER_Extension *ext;
const struct TALER_AgeRestrictionConfig *conf;
ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
if ((NULL == ext) ||
(NULL == ext->config))
return (struct TALER_AgeMask) {0}
;
conf = ext->config;
return conf->mask;
}
/* end age_restriction_helper.c */

View File

@ -1,409 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2021-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file extension_age_restriction.c
* @brief Utility functions regarding age restriction
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_extensions.h"
#include "stdint.h"
/**
* Carries all the information we need for age restriction
*/
struct age_restriction_config
{
struct TALER_AgeMask mask;
size_t num_groups;
};
/**
* Global config for this extension
*/
static struct age_restriction_config TE_age_restriction_config = {0};
enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (
const char *groups,
struct TALER_AgeMask *mask)
{
const char *pos = groups;
unsigned int prev = 0;
unsigned int val = 0;
char c;
while (*pos)
{
c = *pos++;
if (':' == c)
{
if (prev >= val)
return GNUNET_SYSERR;
mask->bits |= 1 << val;
prev = val;
val = 0;
continue;
}
if ('0'>c || '9'<c)
return GNUNET_SYSERR;
val = 10 * val + c - '0';
if (0>=val || 32<=val)
return GNUNET_SYSERR;
}
if (32<=val || prev>=val)
return GNUNET_SYSERR;
mask->bits |= (1 << val);
mask->bits |= 1; // mark zeroth group, too
return GNUNET_OK;
}
char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask)
{
uint32_t bits = mask->bits;
unsigned int n = 0;
char *buf = GNUNET_malloc (32 * 3); // max characters possible
char *pos = buf;
if (NULL == buf)
{
return buf;
}
while (bits != 0)
{
bits >>= 1;
n++;
if (0 == (bits & 1))
{
continue;
}
if (n > 9)
{
*(pos++) = '0' + n / 10;
}
*(pos++) = '0' + n % 10;
if (0 != (bits >> 1))
{
*(pos++) = ':';
}
}
return buf;
}
/* ==================================================
*
* Age Restriction TALER_Extension implementation
*
* ==================================================
*/
/**
* @brief implements the TALER_Extension.disable interface.
*
* @param ext Pointer to the current extension
*/
static void
age_restriction_disable (
struct TALER_Extension *ext)
{
if (NULL == ext)
return;
ext->config = NULL;
if (NULL != ext->config_json)
{
json_decref (ext->config_json);
ext->config_json = NULL;
}
TE_age_restriction_config.mask.bits = 0;
TE_age_restriction_config.num_groups = 0;
}
/**
* @brief implements the TALER_Extension.load_taler_config interface.
*
* @param ext Pointer to the current extension
* @param cfg Handle to the GNUNET configuration
* @return Error if extension for age restriction was set, but age groups were
* invalid, OK otherwise.
*/
static enum GNUNET_GenericReturnValue
age_restriction_load_taler_config (
struct TALER_Extension *ext,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *groups = NULL;
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct TALER_AgeMask mask = {0};
if ((GNUNET_YES !=
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED"))
||
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_yesno (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED")))
{
/* Age restriction is not enabled */
ext->config = NULL;
ext->config_json = NULL;
return GNUNET_OK;
}
/* Age restriction is enabled, extract age groups */
if ((GNUNET_YES ==
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS"))
&&
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_string (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS",
&groups)))
return GNUNET_SYSERR;
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
ret = GNUNET_OK;
if (groups != NULL)
{
ret = TALER_parse_age_group_string (groups, &mask);
if (GNUNET_OK != ret)
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
}
if (GNUNET_OK == ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"setting age mask to %x with #groups: %d\n", mask.bits,
__builtin_popcount (mask.bits) - 1);
TE_age_restriction_config.mask.bits = mask.bits;
TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
ext->config = &TE_age_restriction_config;
/* Note: we do now have TE_age_restriction_config set, however
* ext->config_json is NOT set, i.e. the extension is not yet active! For
* age restriction to become active, load_json_config must have been
* called. */
}
GNUNET_free (groups);
return ret;
}
/**
* @brief implements the TALER_Extension.load_json_config interface.
*
* @param ext if NULL, only tests the configuration
* @param jconfig the configuration as json
*/
static enum GNUNET_GenericReturnValue
age_restriction_load_json_config (
struct TALER_Extension *ext,
json_t *jconfig)
{
struct TALER_AgeMask mask = {0};
enum GNUNET_GenericReturnValue ret;
ret = TALER_JSON_parse_age_groups (jconfig, &mask);
if (GNUNET_OK != ret)
return ret;
/* only testing the parser */
if (ext == NULL)
return GNUNET_OK;
if (TALER_Extension_AgeRestriction != ext->type)
return GNUNET_SYSERR;
TE_age_restriction_config.mask.bits = mask.bits;
TE_age_restriction_config.num_groups = 0;
if (mask.bits > 0)
{
/* if the mask is not zero, the first bit MUST be set */
if (0 == (mask.bits & 1))
return GNUNET_SYSERR;
TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1;
}
ext->config = &TE_age_restriction_config;
if (NULL != ext->config_json)
json_decref (ext->config_json);
ext->config_json = jconfig;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"loaded new age restriction config with age groups: %s\n",
TALER_age_mask_to_string (&mask));
return GNUNET_OK;
}
/**
* @brief implements the TALER_Extension.config_to_json interface.
*
* @param ext if NULL, only tests the configuration
* @return configuration as json_t* object, maybe NULL
*/
static json_t *
age_restriction_config_to_json (
const struct TALER_Extension *ext)
{
char *mask_str;
json_t *conf;
GNUNET_assert (NULL != ext);
if (NULL == ext->config)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"age restriction not configured");
return json_null ();
}
if (NULL != ext->config_json)
{
return json_copy (ext->config_json);
}
mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask);
conf = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("age_groups", mask_str)
);
return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical),
GNUNET_JSON_pack_string ("version", ext->version),
GNUNET_JSON_pack_object_steal ("config", conf)
);
}
/**
* @brief implements the TALER_Extension.test_json_config interface.
*
* @param config configuration as json_t* to test
* @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise.
*/
static enum GNUNET_GenericReturnValue
age_restriction_test_json_config (
const json_t *config)
{
struct TALER_AgeMask mask = {0};
return TALER_JSON_parse_age_groups (config, &mask);
}
/* The extension for age restriction */
struct TALER_Extension TE_age_restriction = {
.next = NULL,
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.version = "1",
.config = NULL, // disabled per default
.config_json = NULL,
.disable = &age_restriction_disable,
.test_json_config = &age_restriction_test_json_config,
.load_json_config = &age_restriction_load_json_config,
.config_to_json = &age_restriction_config_to_json,
.load_taler_config = &age_restriction_load_taler_config,
};
enum GNUNET_GenericReturnValue
TALER_extension_age_restriction_register ()
{
return TALER_extensions_add (&TE_age_restriction);
}
bool
TALER_extensions_age_restriction_is_configured ()
{
return (0 != TE_age_restriction_config.mask.bits);
}
struct TALER_AgeMask
TALER_extensions_age_restriction_ageMask ()
{
return TE_age_restriction_config.mask;
}
size_t
TALER_extensions_age_restriction_num_groups ()
{
return TE_age_restriction_config.num_groups;
}
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask)
{
enum GNUNET_GenericReturnValue ret;
const char *str;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("age_groups",
&str),
GNUNET_JSON_spec_end ()
};
ret = GNUNET_JSON_parse (root,
spec,
NULL,
NULL);
if (GNUNET_OK == ret)
TALER_parse_age_group_string (str, mask);
GNUNET_JSON_parse_free (spec);
return ret;
}
/* end of extension_age_restriction.c */

View File

@ -24,51 +24,53 @@
#include "taler_extensions.h"
#include "stdint.h"
/* head of the list of all registered extensions */
static struct TALER_Extension *TE_extensions = NULL;
static struct TALER_Extensions TE_extensions = {
.next = NULL,
.extension = NULL,
};
const struct TALER_Extension *
const struct TALER_Extensions *
TALER_extensions_get_head ()
{
return TE_extensions;
return &TE_extensions;
}
enum GNUNET_GenericReturnValue
TALER_extensions_add (
struct TALER_Extension *extension)
static enum GNUNET_GenericReturnValue
add_extension (
const struct TALER_Extension *extension)
{
/* Sanity checks */
if ((NULL == extension) ||
(NULL == extension->name) ||
(NULL == extension->version) ||
(NULL == extension->disable) ||
(NULL == extension->test_json_config) ||
(NULL == extension->load_json_config) ||
(NULL == extension->config_to_json) ||
(NULL == extension->load_taler_config))
(NULL == extension->load_config) ||
(NULL == extension->manifest))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"invalid extension\n");
return GNUNET_SYSERR;
}
if (NULL == TE_extensions) /* first extension ?*/
TE_extensions = (struct TALER_Extension *) extension;
if (NULL == TE_extensions.extension) /* first extension ?*/
TE_extensions.extension = extension;
else
{
struct TALER_Extension *iter;
struct TALER_Extension *last;
struct TALER_Extensions *iter;
struct TALER_Extensions *last;
/* Check for collisions */
for (iter = TE_extensions; NULL != iter; iter = iter->next)
for (iter = &TE_extensions;
NULL != iter && NULL != iter->extension;
iter = iter->next)
{
const struct TALER_Extension *ext = iter->extension;
last = iter;
if (extension->type == iter->type ||
if (extension->type == ext->type ||
0 == strcasecmp (extension->name,
iter->name))
ext->name))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"extension collision for `%s'\n",
@ -78,7 +80,11 @@ TALER_extensions_add (
}
/* No collisions found, so add this extension to the list */
last->next = extension;
{
struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions);
extn->extension = extension;
last->next = extn;
}
}
return GNUNET_OK;
@ -89,12 +95,12 @@ const struct TALER_Extension *
TALER_extensions_get_by_type (
enum TALER_Extension_Type type)
{
for (const struct TALER_Extension *it = TE_extensions;
NULL != it;
for (const struct TALER_Extensions *it = &TE_extensions;
NULL != it && NULL != it->extension;
it = it->next)
{
if (it->type == type)
return it;
if (it->extension->type == type)
return it->extension;
}
/* No extension found. */
@ -109,8 +115,7 @@ TALER_extensions_is_enabled_type (
const struct TALER_Extension *ext =
TALER_extensions_get_by_type (type);
return (NULL != ext &&
TALER_extensions_is_enabled (ext));
return (NULL != ext && ext->enabled);
}
@ -118,33 +123,34 @@ const struct TALER_Extension *
TALER_extensions_get_by_name (
const char *name)
{
for (const struct TALER_Extension *it = TE_extensions;
for (const struct TALER_Extensions *it = &TE_extensions;
NULL != it;
it = it->next)
{
if (0 == strcasecmp (name, it->name))
return it;
if (0 == strcasecmp (name, it->extension->name))
return it->extension;
}
/* No extension found. */
/* No extension found, try to load it. */
return NULL;
}
enum GNUNET_GenericReturnValue
TALER_extensions_verify_json_config_signature (
json_t *extensions,
TALER_extensions_verify_manifests_signature (
json_t *manifests,
struct TALER_MasterSignatureP *extensions_sig,
struct TALER_MasterPublicKeyP *master_pub)
{
struct TALER_ExtensionConfigHashP h_config;
struct TALER_ExtensionManifestsHashP h_manifests;
if (GNUNET_OK !=
TALER_JSON_extensions_config_hash (extensions,
&h_config))
TALER_JSON_extensions_manifests_hash (manifests,
&h_manifests))
return GNUNET_SYSERR;
if (GNUNET_OK !=
TALER_exchange_offline_extension_config_hash_verify (
&h_config,
TALER_exchange_offline_extension_manifests_hash_verify (
&h_manifests,
master_pub,
extensions_sig))
return GNUNET_NO;
@ -178,7 +184,8 @@ configure_extension (
{
struct LoadConfClosure *col = cls;
const char *name;
const struct TALER_Extension *extension;
char *lib_name;
struct TALER_Extension *extension;
if (GNUNET_OK != col->error)
return;
@ -190,33 +197,49 @@ configure_extension (
name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
if (NULL ==
(extension = TALER_extensions_get_by_name (name)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unsupported extension `%s` (section [%s]).\n", name,
section);
col->error = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
extension->load_taler_config (
(struct TALER_Extension *) extension,
col->cfg))
/* Load the extension library */
GNUNET_asprintf (&lib_name,
"libtaler_extension_%s",
name);
extension = GNUNET_PLUGIN_load (
lib_name,
(void *) col->cfg);
if (NULL == extension)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Couldn't parse configuration for extension `%s` (section [%s]).\n",
"Couldn't load extension library to `%s` (section [%s]).\n",
name,
section);
col->error = GNUNET_SYSERR;
return;
}
if (GNUNET_OK != add_extension (extension))
{
/* TODO: Ignoring return values here */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Couldn't add extension `%s` (section [%s]).\n",
name,
section);
col->error = GNUNET_SYSERR;
GNUNET_PLUGIN_unload (
lib_name,
(void *) col->cfg);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"extension library '%s' loaded\n",
lib_name);
}
static bool extensions_loaded = false;
enum GNUNET_GenericReturnValue
TALER_extensions_load_taler_config (
TALER_extensions_init (
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
struct LoadConfClosure col = {
@ -224,15 +247,22 @@ TALER_extensions_load_taler_config (
.error = GNUNET_OK,
};
if (extensions_loaded)
return GNUNET_OK;
GNUNET_CONFIGURATION_iterate_sections (cfg,
&configure_extension,
&col);
if (GNUNET_OK == col.error)
extensions_loaded = true;
return col.error;
}
enum GNUNET_GenericReturnValue
TALER_extensions_is_json_config (
TALER_extensions_parse_manifest (
json_t *obj,
int *critical,
const char **version,
@ -265,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 ());
}

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

View File

@ -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,
&currency))
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 */

View File

@ -233,7 +233,7 @@ typedef void
*
* @param auditor the auditor handle; the auditor must be ready to operate
* @param h_wire hash of merchant wire details
* @param h_extensions hash over the extensions, if any
* @param h_policy hash over the policy, if any
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param wire_deadline date until which the exchange should wire the funds
@ -257,7 +257,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle *
TALER_AUDITOR_deposit_confirmation (
struct TALER_AUDITOR_Handle *auditor,
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_ExtensionContractHashP *h_extensions,
const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_PrivateContractHashP *h_contract_terms,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,

View File

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

View File

@ -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 merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment, if applicable to the denomination (maybe NULL)
* @param h_extensions hash over the extensions
* @param h_policy hash over the policy
* @param h_denom_pub hash of the coin denomination's public key
* @param coin_priv coins private key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
@ -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 merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment (maybe all zeroes, if not applicable to the denomination)
* @param h_extensions hash over the extensions
* @param h_policy hash over the policy
* @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 merchants 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 merchants 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
);

View File

@ -26,6 +26,7 @@
#include <gnunet/gnunet_db_lib.h>
#include "taler_json_lib.h"
#include "taler_signatures.h"
#include "taler_extensions_policy.h"
/**
@ -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);
/**

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA
Copyright (C) 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -24,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

View File

@ -0,0 +1,198 @@
/*
This file is part of TALER
Copyright (C) 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file include/taler_extensions_policy.h
* @brief Interface for policy extensions
* @author Özgür Kesim
*/
#ifndef TALER_EXTENSIONS_POLICY_H
#define TALER_EXTENSIONS_POLICY_H
#include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
/*
* @brief Describes the states of fulfillment of a policy bound to a deposit
*/
enum TALER_PolicyFulfillmentState
{
/* General error state of an fulfillment. */
TALER_PolicyFulfillmentFailure = 0,
/* The policy is not yet ready due to insufficient funding. More deposits are
* necessary for it to become ready . */
TALER_PolicyFulfillmentInsufficient = 1,
/* The policy is funded and ready, pending */
TALER_PolicyFulfillmentReady = 2,
/* Policy is provably fulfilled. */
TALER_PolicyFulfillmentSuccess = 3,
/* Policy fulfillment has timed out */
TALER_PolicyFulfillmentTimeout = 4,
TALER_PolicyFulfillmentStateCount = TALER_PolicyFulfillmentTimeout + 1
};
/*
* @brief Returns a string representation of the state of a policy fulfillment
*/
const char *
TALER_policy_fulfillment_state_str (enum TALER_PolicyFulfillmentState state);
/* @brief Details of a policy for a deposit request */
struct TALER_PolicyDetails
{
/* Hash code that should be used for the .policy_hash_code field when
* this policy is saved in the policy_details table. */
struct GNUNET_HashCode hash_code;
/* Content of the policy in its original JSON form */
json_t *policy_json;
/* When the deadline is meat and the policy is still in "Ready" state,
* a timeout-handler will transfer the amount
* (total_amount - policy_fee - refreshable_amount)
* to the payto-URI from the corresponding deposit. The value
* amount_refreshable will be refreshable by the owner of the
* associated deposits's coins */
struct GNUNET_TIME_Timestamp deadline;
/* The amount to which this policy commits to. It must be at least as
* large as @e policy_fee. */
struct TALER_Amount commitment;
/* The total sum of contributions from coins so far to fund this
* policy. It must be at least as large as @commitment in order to be
* sufficiently funded. */
struct TALER_Amount accumulated_total;
/* The fee from the exchange for handling the policy. It is due when
* the state changes to Timeout or Success. */
struct TALER_Amount policy_fee;
/* The amount that will be transfered to the payto-URIs from the
* corresponding deposits when the fulfillment state changes to Timeout
* or Success. Note that a fulfillment handler can alter this upon
* arrival of a proof of fulfillment. The remaining amount
* (accumulated_amount - policy_amount - transferable_amount) */
struct TALER_Amount transferable_amount;
/* The state of fulfillment of a policy.
* - If the state is Insufficient, the client is required to call
* /deposit -maybe multiple times- with enough coins and the same
* policy details in order to reach the required amount. The state is
* then changed to Ready.
* - If the state changes to Timeout or Success, a handler will transfer
* the amount (total_amount - policy_fee - refreshable_amount) to the
* payto-URI from the corresponding deposit. The value
* amount_refreshable will be refreshable by the owner of the
* associated deposits's coins. */
enum TALER_PolicyFulfillmentState fulfillment_state;
/* If there is a proof of fulfillment, the row ID from the
* policy_fulfillment table */
uint64_t policy_fulfillment_id;
bool no_policy_fulfillment_id;
};
/*
* @brief All information required for the database transaction when handling a
* proof of fulfillment request.
*/
struct TALER_PolicyFulfillmentTransactionData
{
/* The incoming proof, provided by a client */
const json_t *proof;
/* The Hash of the proof */
struct GNUNET_HashCode h_proof;
/* The timestamp of retrieval of the proof */
struct GNUNET_TIME_Timestamp timestamp;
/* The ID of the proof in the policy_fulfillment table. Will be set
* during the transaction. Needed to fill the table
* policy_details_fulfillments. */
uint64_t fulfillment_id;
/* The list of policy details. Will be updated by the policy handler */
struct TALER_PolicyDetails *details;
size_t details_count;
};
/*
* @brief Extracts policy details from the deposit's policy options and the policy extensions
*
* @param[in] policy_options JSON of the policy options from a deposit request
* @param[out] details On GNUNET_OK, the parsed details
* @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed
* @return GNUNET_OK on success, GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was
* invalid, with *error_hint maybe non-NULL.
*/
enum GNUNET_GenericReturnValue
TALER_extensions_create_policy_details (
const json_t *policy_options,
struct TALER_PolicyDetails *details,
const char **error_hint);
/*
* ================================
* Merchant refund policy
* ================================
*/
struct TALER_ExtensionPolicyMerchantRefundPolicyConfig
{
struct GNUNET_TIME_Relative max_timeout;
};
/*
* ================================
* Brandt-Vickrey Auctions policy
* ================================
*/
/*
* @brief Configuration for Brandt-Vickrey auctions policy
*/
struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig
{
uint16_t max_bidders;
uint16_t max_prices;
struct TALER_Amount auction_fee;
};
/*
* ================================
* Escrowed Payments policy
* ================================
*/
/*
* @brief Configuration for escrowed payments policy
*/
struct TALER_ExtensionPolicyEscrowedPaymentsConfig
{
struct GNUNET_TIME_Relative max_timeout;
};
#endif

View File

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

View File

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

View File

@ -1008,12 +1008,12 @@ TALER_JSON_get_error_code2 (const void *data,
void
TALER_deposit_extension_hash (const json_t *extensions,
struct TALER_ExtensionContractHashP *ech)
TALER_deposit_policy_hash (const json_t *policy,
struct TALER_ExtensionPolicyHashP *ech)
{
GNUNET_assert (GNUNET_OK ==
dump_and_hash (extensions,
"taler-contract-extensions",
dump_and_hash (policy,
"taler-extensions-policy",
&ech->hash));
}
@ -1037,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);
}

View File

@ -153,7 +153,7 @@ handle_deposit_confirmation_finished (void *cls,
* Verify signature information about the deposit-confirmation.
*
* @param h_wire hash of merchant wire details
* @param h_extensions hash over the extensions, if any
* @param h_policy hash over the 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",

View File

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

View File

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

View File

@ -203,7 +203,7 @@ TALER_EXCHANGE_get_min_denomination_ (
* Verify signature information about the deposit.
*
* @param dcd contract details
* @param ech hashed contract (passed to avoid recomputation)
* @param 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);

View File

@ -97,9 +97,9 @@ struct TALER_EXCHANGE_DepositHandle
struct TALER_MerchantWireHashP h_wire;
/**
* Hash over the extensions, or all zero.
* Hash over the policy, 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))

View File

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

View File

@ -236,7 +236,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
struct TALER_PrivateContractHashP h_contract_terms;
struct TALER_AgeCommitmentHash h_age_commitment;
bool no_hac;
// struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
struct TALER_ExtensionPolicyHashP h_policy;
bool no_h_policy;
struct GNUNET_TIME_Timestamp wallet_timestamp;
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_TIME_Timestamp refund_deadline;
@ -252,6 +253,10 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&h_age_commitment),
&no_hac),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_policy",
&h_policy),
&no_h_policy),
GNUNET_JSON_spec_timestamp ("timestamp",
&wallet_timestamp),
GNUNET_JSON_spec_timestamp ("refund_deadline",
@ -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

File diff suppressed because it is too large Load Diff

3224
src/testing/fnord Normal file

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

View File

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

View 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

View File

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

View File

@ -199,7 +199,7 @@ deposit_confirmation_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
static struct TALER_ExtensionContractHashP no_h_extensions;
static struct TALER_ExtensionPolicyHashP no_h_policy;
struct DepositConfirmationState *dcs = cls;
const struct TALER_TESTING_Command *deposit_cmd;
struct TALER_MerchantWireHashP h_wire;
@ -310,7 +310,7 @@ deposit_confirmation_run (void *cls,
}
dcs->dc = TALER_AUDITOR_deposit_confirmation (dcs->auditor,
&h_wire,
&no_h_extensions,
&no_h_policy,
&h_contract_terms,
*exchange_timestamp,
*wire_deadline,

View File

@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash);
mask = TALER_extensions_age_restriction_ageMask ();
mask = TALER_extensions_get_age_restriction_mask ();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed,
sizeof(seed));

View File

@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash);
mask = TALER_extensions_age_restriction_ageMask ();
mask = TALER_extensions_get_age_restriction_mask ();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed,
sizeof(seed));

View File

@ -314,8 +314,13 @@ sign_keys_for_exchange (void *cls,
char *exchange_master_pub;
int ret;
/* Load the age restriction mask from the configuration */
TALER_extensions_load_taler_config (cfg);
/* Load the extensions */
if (GNUNET_OK != TALER_extensions_init (cfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"couldn't load extensions");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
@ -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",

View File

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

View File

@ -21,6 +21,7 @@
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
#include <gnunet/gnunet_json_lib.h>
#include <gcrypt.h>
void
@ -436,3 +437,113 @@ TALER_age_commitment_proof_free (
cp->commitment.keys = NULL;
}
}
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask)
{
enum GNUNET_GenericReturnValue ret;
const char *str;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("age_groups",
&str),
GNUNET_JSON_spec_end ()
};
ret = GNUNET_JSON_parse (root,
spec,
NULL,
NULL);
if (GNUNET_OK == ret)
TALER_parse_age_group_string (str, mask);
GNUNET_JSON_parse_free (spec);
return ret;
}
enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (
const char *groups,
struct TALER_AgeMask *mask)
{
const char *pos = groups;
unsigned int prev = 0;
unsigned int val = 0;
char c;
while (*pos)
{
c = *pos++;
if (':' == c)
{
if (prev >= val)
return GNUNET_SYSERR;
mask->bits |= 1 << val;
prev = val;
val = 0;
continue;
}
if ('0'>c || '9'<c)
return GNUNET_SYSERR;
val = 10 * val + c - '0';
if (0>=val || 32<=val)
return GNUNET_SYSERR;
}
if (32<=val || prev>=val)
return GNUNET_SYSERR;
mask->bits |= (1 << val);
mask->bits |= 1; // mark zeroth group, too
return GNUNET_OK;
}
char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask)
{
uint32_t bits = mask->bits;
unsigned int n = 0;
char *buf = GNUNET_malloc (32 * 3); // max characters possible
char *pos = buf;
if (NULL == buf)
{
return buf;
}
while (bits != 0)
{
bits >>= 1;
n++;
if (0 == (bits & 1))
{
continue;
}
if (n > 9)
{
*(pos++) = '0' + n / 10;
}
*(pos++) = '0' + n % 10;
if (0 != (bits >> 1))
{
*(pos++) = ':';
}
}
return buf;
}
/* end util/age_restriction.c */

View File

@ -48,10 +48,10 @@ struct TALER_DepositConfirmationPS
struct TALER_MerchantWireHashP h_wire GNUNET_PACKED;
/**
* Hash over the extension options of the deposit, 0 if there
* were not extension options.
* Hash over the 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 !=

View File

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

View File

@ -49,9 +49,9 @@ struct TALER_DepositRequestPS
struct TALER_AgeCommitmentHash h_age_commitment GNUNET_PACKED;
/**
* Hash over extension attributes shared with the exchange.
* Hash over 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,