summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-11-04 12:18:16 +0100
committerÖzgür Kesim <oec-taler@kesim.org>2022-11-04 12:18:16 +0100
commit752f10273860d2496fc3eb1e03de6ad4451e7c0f (patch)
tree53d51969f58611dbf8afacdcd40a769f5c847dd8 /src/include
parentc89bfa9026d7180eb24ae9480f225b93db22c53a (diff)
downloadexchange-752f10273860d2496fc3eb1e03de6ad4451e7c0f.tar.gz
exchange-752f10273860d2496fc3eb1e03de6ad4451e7c0f.tar.bz2
exchange-752f10273860d2496fc3eb1e03de6ad4451e7c0f.zip
policy extensions and age restriction refactoring
- refactoring of extension-plugin-mechanism - refactoring of age restriction extension - added policy extensions plugin plumbing - added DB schema and api - policy_details - policy_fulfillments
Diffstat (limited to 'src/include')
-rw-r--r--src/include/taler_auditor_service.h4
-rw-r--r--src/include/taler_auditordb_plugin.h4
-rw-r--r--src/include/taler_crypto_lib.h43
-rw-r--r--src/include/taler_exchange_service.h4
-rw-r--r--src/include/taler_exchangedb_plugin.h135
-rw-r--r--src/include/taler_extensions.h345
-rw-r--r--src/include/taler_extensions_policy.h198
-rw-r--r--src/include/taler_json_lib.h20
-rw-r--r--src/include/taler_util.h58
9 files changed, 616 insertions, 195 deletions
diff --git a/src/include/taler_auditor_service.h b/src/include/taler_auditor_service.h
index 30d18e6e9..c20b789cc 100644
--- a/src/include/taler_auditor_service.h
+++ b/src/include/taler_auditor_service.h
@@ -233,7 +233,7 @@ typedef void
*
* @param auditor the auditor handle; the auditor must be ready to operate
* @param h_wire hash of merchant wire details
- * @param h_extensions hash over the extensions, if any
+ * @param h_policy hash over the policy, if any
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param wire_deadline date until which the exchange should wire the funds
@@ -257,7 +257,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle *
TALER_AUDITOR_deposit_confirmation (
struct TALER_AUDITOR_Handle *auditor,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_PrivateContractHashP *h_contract_terms,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h
index 9f46004c1..cf27668b6 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -382,9 +382,9 @@ struct TALER_AUDITORDB_DepositConfirmation
struct TALER_PrivateContractHashP h_contract_terms;
/**
- * Hash over the extensions for the deposit.
+ * Hash over the policy extension for the deposit.
*/
- struct TALER_ExtensionContractHashP h_extensions;
+ struct TALER_ExtensionPolicyHashP h_policy;
/**
* Hash over the wiring information of the merchant.
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 4c478cefd..6b8000933 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -638,10 +638,9 @@ struct TALER_PrivateContractHashP
/**
- * Hash used to represent the "public" extensions to
- * a contract that is shared with the exchange.
+ * Hash used to represent the policy extension to a deposit
*/
-struct TALER_ExtensionContractHashP
+struct TALER_ExtensionPolicyHashP
{
/**
* Actual hash value.
@@ -727,10 +726,10 @@ struct TALER_PickupIdentifierP
/**
- * @brief Salted hash over the JSON object representing the configuration of an
- * extension.
+ * @brief Salted hash over the JSON object representing the manifests of
+ * extensions.
*/
-struct TALER_ExtensionConfigHashP
+struct TALER_ExtensionManifestsHashP
{
/**
* Actual hash value.
@@ -3213,7 +3212,7 @@ TALER_wallet_reserve_attest_request_verify (
* @param h_wire hash of the merchant’s account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment, if applicable to the denomination (maybe NULL)
- * @param h_extensions hash over the extensions
+ * @param h_policy hash over the policy extension
* @param h_denom_pub hash of the coin denomination's public key
* @param coin_priv coin’s private key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
@@ -3228,7 +3227,7 @@ TALER_wallet_deposit_sign (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_DenominationHashP *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -3245,7 +3244,7 @@ TALER_wallet_deposit_sign (
* @param h_wire hash of the merchant’s account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_age_commitment hash over the age commitment (maybe all zeroes, if not applicable to the denomination)
- * @param h_extensions hash over the extensions
+ * @param h_policy hash over the policy extension
* @param h_denom_pub hash of the coin denomination's public key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
@@ -3261,7 +3260,7 @@ TALER_wallet_deposit_verify (
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_AgeCommitmentHash *h_age_commitment,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
const struct TALER_DenominationHashP *h_denom_pub,
struct GNUNET_TIME_Timestamp wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -3666,7 +3665,7 @@ typedef enum TALER_ErrorCode
* @param scb function to call to create the signature
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_wire hash of the merchant’s account details
- * @param h_extensions hash over the extensions, can be NULL
+ * @param h_policy hash over the policy extension, can be NULL
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far off
* @param wire_deadline date until which the exchange should wire the funds
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
@@ -3682,7 +3681,7 @@ TALER_exchange_online_deposit_confirmation_sign (
TALER_ExchangeSignCallback scb,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -3698,7 +3697,7 @@ TALER_exchange_online_deposit_confirmation_sign (
*
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param h_wire hash of the merchant’s account details
- * @param h_extensions hash over the extensions, can be NULL
+ * @param h_policy hash over the policy extension, can be NULL
* @param exchange_timestamp timestamp when the contract was finalized, must not be too far off
* @param wire_deadline date until which the exchange should wire the funds
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
@@ -3713,7 +3712,7 @@ enum GNUNET_GenericReturnValue
TALER_exchange_online_deposit_confirmation_verify (
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantWireHashP *h_wire,
- const struct TALER_ExtensionContractHashP *h_extensions,
+ const struct TALER_ExtensionPolicyHashP *h_policy,
struct GNUNET_TIME_Timestamp exchange_timestamp,
struct GNUNET_TIME_Timestamp wire_deadline,
struct GNUNET_TIME_Timestamp refund_deadline,
@@ -5257,31 +5256,31 @@ TALER_merchant_contract_sign (
/* **************** /management/extensions offline signing **************** */
/**
- * Create a signature for the hash of the configuration of an extension
+ * Create a signature for the hash of the manifests of extensions
*
- * @param h_config hash of the JSON object representing the configuration
+ * @param h_manifests hash of the JSON object representing the manifests
* @param master_priv private key to sign with
* @param[out] master_sig where to write the signature
*/
void
-TALER_exchange_offline_extension_config_hash_sign (
- const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_sign (
+ const struct TALER_ExtensionManifestsHashP *h_manifests,
const struct TALER_MasterPrivateKeyP *master_priv,
struct TALER_MasterSignatureP *master_sig);
/**
* Verify the signature in @a master_sig of the given hash, taken over the JSON
- * blob representing the configuration of an extension
+ * blob representing the manifests of extensions
*
- * @param h_config hash of the JSON blob of a configuration of an extension
+ * @param h_manifest hash of the JSON blob of manifests of extensions
* @param master_pub master public key of the exchange
* @param master_sig signature of the exchange
* @return #GNUNET_OK if signature is valid
*/
enum GNUNET_GenericReturnValue
-TALER_exchange_offline_extension_config_hash_verify (
- const struct TALER_ExtensionConfigHashP *h_config,
+TALER_exchange_offline_extension_manifests_hash_verify (
+ const struct TALER_ExtensionManifestsHashP *h_manifest,
const struct TALER_MasterPublicKeyP *master_pub,
const struct TALER_MasterSignatureP *master_sig
);
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index aa9a5b607..e3a349a92 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -894,9 +894,9 @@ struct TALER_EXCHANGE_DepositContractDetail
struct TALER_PrivateContractHashP h_contract_terms;
/**
- * Extension-specific details about the deposit relevant to the exchange.
+ * Policy extension specific details about the deposit relevant to the exchange.
*/
- const json_t *extension_details;
+ json_t *policy_details;
/**
* Timestamp when the contract was finalized, must match approximately the
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 9640e052f..f21301e7e 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -26,6 +26,7 @@
#include <gnunet/gnunet_db_lib.h>
#include "taler_json_lib.h"
#include "taler_signatures.h"
+#include "taler_extensions_policy.h"
/**
@@ -220,7 +221,8 @@ enum TALER_EXCHANGEDB_ReplicatedTable
TALER_EXCHANGEDB_RT_RECOUP,
TALER_EXCHANGEDB_RT_RECOUP_REFRESH,
TALER_EXCHANGEDB_RT_EXTENSIONS,
- TALER_EXCHANGEDB_RT_EXTENSION_DETAILS,
+ TALER_EXCHANGEDB_RT_POLICY_DETAILS,
+ TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS,
TALER_EXCHANGEDB_RT_PURSE_REQUESTS,
TALER_EXCHANGEDB_RT_PURSE_DECISION,
TALER_EXCHANGEDB_RT_PURSE_MERGES,
@@ -438,8 +440,8 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_WireSaltP wire_salt;
struct TALER_PaytoHashP wire_target_h_payto;
- bool extension_blocked;
- uint64_t extension_details_serial_id;
+ bool policy_blocked;
+ uint64_t policy_details_serial_id;
} deposits;
struct
@@ -510,13 +512,32 @@ struct TALER_EXCHANGEDB_TableData
struct
{
char *name;
- char *config;
+ char *manifest;
} extensions;
struct
{
- char *extension_options;
- } extension_details;
+ struct GNUNET_HashCode hash_code;
+ json_t *policy_json;
+ bool no_policy_json;
+ struct GNUNET_TIME_Timestamp deadline;
+ struct TALER_Amount commitment;
+ struct TALER_Amount accumulated_total;
+ struct TALER_Amount fee;
+ struct TALER_Amount transferable;
+ uint16_t fulfillment_state; /* will also be recomputed */
+ uint64_t fulfillment_id;
+ bool no_fulfillment_id;
+ } policy_details;
+
+ struct
+ {
+ struct GNUNET_TIME_Timestamp fulfillment_timestamp;
+ char *fulfillment_proof;
+ struct GNUNET_HashCode h_fulfillment_proof;
+ struct GNUNET_HashCode *policy_hash_codes;
+ size_t policy_hash_codes_count;
+ } policy_fulfillments;
struct
{
@@ -1512,12 +1533,6 @@ struct TALER_EXCHANGEDB_Deposit
char *receiver_wire_account;
/**
- * Additional details for extensions relevant for this
- * deposit operation, possibly NULL!
- */
- json_t *extension_details;
-
- /**
* Time when this request was generated. Used, for example, to
* assess when (roughly) the income was achieved for tax purposes.
* Note that the Exchange will only check that the timestamp is not "too
@@ -1558,6 +1573,16 @@ struct TALER_EXCHANGEDB_Deposit
*/
struct TALER_Amount deposit_fee;
+ /*
+ * True if @e policy_json was provided
+ */
+ bool has_policy;
+
+ /**
+ * Hash over the policy data for this deposit (remains unknown to the
+ * Exchange). Needed for the verification of the deposit's signature
+ */
+ struct TALER_ExtensionPolicyHashP h_policy;
};
@@ -1656,6 +1681,17 @@ struct TALER_EXCHANGEDB_DepositListEntry
*/
struct TALER_Amount deposit_fee;
+ /*
+ * True if a policy was provided with the deposit request
+ */
+ bool has_policy;
+
+ /**
+ * Hash over the policy data for this deposit (remains unknown to the
+ * Exchange). Needed for the verification of the deposit's signature
+ */
+ struct TALER_ExtensionPolicyHashP h_policy;
+
/**
* Has the deposit been wired?
*/
@@ -3530,6 +3566,40 @@ struct TALER_EXCHANGEDB_Plugin
bool *conflict,
bool *nonce_reuse);
+ /**
+ * Retrieve the details to a policy given by its hash_code
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param hc Hash code that identifies the policy
+ * @param[out] detail retrieved policy details
+ * @return query execution status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*get_policy_details)(
+ void *cls,
+ const struct GNUNET_HashCode *hc,
+ struct TALER_PolicyDetails *detail);
+
+ /**
+ * Persist the policy details that extends a deposit. The particular policy
+ * - referenced by details->hash_code - might already exist in the table, in
+ * which case the call will update the contents of the record with @e details
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param details The parsed `struct TALER_PolicyDetails` according to the responsible policy extension.
+ * @param[out] policy_details_serial_id The ID of the entry in the policy_details table
+ * @param[out] accumulated_total The total amount accumulated in that policy
+ * @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready.
+ * @return query execution status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*persist_policy_details)(
+ void *cls,
+ const struct TALER_PolicyDetails *details,
+ uint64_t *policy_details_serial_id,
+ struct TALER_Amount *accumulated_total,
+ enum TALER_PolicyFulfillmentState *fulfillment_state);
+
/**
* Perform deposit operation, checking for sufficient balance
@@ -3539,7 +3609,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param deposit deposit operation details
* @param known_coin_id row of the coin in the known_coins table
* @param h_payto hash of the merchant's payto URI
- * @param extension_blocked true if an extension is blocking the wire transfer
+ * @param policy_details_serial_id (pointer to) the row ID of the policy details, maybe NULL
* @param[in,out] exchange_timestamp time to use for the deposit (possibly updated)
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] in_conflict set to true if the deposit conflicted
@@ -3551,7 +3621,7 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_EXCHANGEDB_Deposit *deposit,
uint64_t known_coin_id,
const struct TALER_PaytoHashP *h_payto,
- bool extension_blocked,
+ uint64_t *policy_details_serial_id,
struct GNUNET_TIME_Timestamp *exchange_timestamp,
bool *balance_ok,
bool *in_conflict);
@@ -3581,6 +3651,19 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * Add a proof of fulfillment of an policy
+ *
+ * @param cls the plugin-specific state
+ * @param[in,out] fulfillment The proof of fulfillment and serial_ids of the policy_details along with their new state and potential new amounts.
+ * @return query execution status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*add_policy_fulfillment_proof)(
+ void *cls,
+ struct TALER_PolicyFulfillmentTransactionData *fulfillment);
+
+
+ /**
* Check if the given @a nonce was properly locked to the given @a old_coin_pub. If so, check if we already
* created CS signatures for the given @a nonce and @a new_denom_pub_hashes,
* and if so, return them in @a s_scalars. Otherwise, persist the
@@ -5559,33 +5642,33 @@ struct TALER_EXCHANGEDB_Plugin
/**
- * Function called to save the configuration of an extension
- * (age-restriction, peer2peer, ...)
+ * Function called to save the manifest of an extension
+ * (age-restriction, policy-extension, ...)
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param config JSON object of the configuration as string, maybe NULL (== disabled extension)
+ * @param manifest JSON object of the Manifest as string, maybe NULL (== disabled extension)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
- (*set_extension_config)(void *cls,
- const char *extension_name,
- const char *config);
+ (*set_extension_manifest)(void *cls,
+ const char *extension_name,
+ const char *manifest);
/**
- * Function called to retrieve the configuration of an extension
- * (age-restriction, peer2peer, ...)
+ * Function called to retrieve the manifest of an extension
+ * (age-restriction, policy-extension, ...)
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param extension_name the name of the extension
- * @param[out] config JSON object of the configuration as string, maybe NULL (== disabled extension)
+ * @param[out] manifest Manifest of the extension in JSON encoding, maybe NULL (== disabled extension)
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
- (*get_extension_config)(void *cls,
- const char *extension_name,
- char **config);
+ (*get_extension_manifest)(void *cls,
+ const char *extension_name,
+ char **manifest);
/**
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 8e1823cce..5e53d27f8 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -24,48 +24,195 @@
#include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h"
#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_extensions_policy.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
enum TALER_Extension_Type
{
- TALER_Extension_AgeRestriction = 0,
- TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
+ TALER_Extension_PolicyNull = 0,
+
+ TALER_Extension_AgeRestriction = 1,
+ TALER_Extension_PolicyMerchantRefund = 2,
+ TALER_Extension_PolicyBrandtVickeryAuction = 3,
+ TALER_Extension_PolicyEscrowedPayment = 4,
+
+ TALER_Extension_MaxPredefined = 5 // Must be last of the predefined
};
+
+/* Forward declarations */
+enum TALER_PolicyFulfillmentState;
+struct TALER_PolicyFulfillmentOutcome;
+
/*
* @brief Represents the implementation of an extension.
*
- * TODO: add documentation
+ * An "Extension" is an optional feature for the Exchange.
+ * There are only two types of extensions:
+ *
+ * a) Age restriction: This is a special feature that directly interacts with
+ * denominations and coins, but is not define policies during deposits, see b).
+ * The implementation of this extension doesn't have to implement any of the
+ * http- or depost-handlers in the struct.
+ *
+ * b) Policies for deposits: These are extensions that define policies (such
+ * as refund, escrow or auctions) for deposit requests. These extensions have
+ * to implement at least the deposit- and post-http-handler in the struct to be
+ * functional.
+ *
+ * In addition to the handlers defined in this struct, an extension must also
+ * be a plugin in the GNUNET_Plugin sense. That is, it must implement the
+ * functions
+ * 1: (void *ext)libtaler_extension_<name>_init(void *cfg)
+ * and
+ * 2: (void *)libtaler_extension_<name>_done(void *)
+ *
+ * In 1:, the input will be the GNUNET_CONFIGURATION_Handle to the TALER
+ * configuration and the output must be the struct TALER_Extension * on
+ * success, NULL otherwise.
+ *
+ * In 2:, no arguments are passed and NULL is expected to be returned.
*/
struct TALER_Extension
{
- /* simple linked list */
- struct TALER_Extension *next;
-
+ /**
+ * Type of the extension. Only one extension of a type can be loaded
+ * at any time.
+ */
enum TALER_Extension_Type type;
+
+ /**
+ * The name of the extension, must be unique among all loaded extensions. It
+ * is used in URLs for /extension/$NAME as well.
+ */
char *name;
+
+ /**
+ * Criticality of the extension. It has the same semantics as "critical" has
+ * for extensions in X.509:
+ * - if "true", the client must "understand" the extension before proceeding,
+ * - if "false", clients can safely skip extensions they do not understand.
+ * (see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)
+ */
bool critical;
+
+ /**
+ * Version of the extension must be provided in Taler's protocol verison ranges notation, see
+ * https://docs.taler.net/core/api-common.html#protocol-version-ranges
+ */
char *version;
+
+ /**
+ * If the extension is marked as enabled, it will be listed in the
+ * "extensions" field in the "/keys" response.
+ */
+ bool enabled;
+
+ /**
+ * Opaque (public) configuration object, set by the extension.
+ */
void *config;
- json_t *config_json;
- void (*disable)(struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*test_json_config)(
- const json_t *config);
+ /**
+ * @brief Handler to to disable the extension.
+ *
+ * @param ext The current extension object
+ */
+ void (*disable)(struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*load_json_config)(
+ /**
+ * @brief Handler to read an extension-specific configuration in JSON
+ * encoding and enable the extension. Must be implemented by the extension.
+ *
+ * @param ext The extension object. If NULL, the configuration will only be checked.
+ * @param config A JSON blob
+ * @return GNUNET_OK if the json was a valid configuration for the extension.
+ */
+ enum GNUNET_GenericReturnValue (*load_config)(
struct TALER_Extension *ext,
json_t *config);
- json_t *(*config_to_json)(
+ /**
+ * @brief Handler to return the manifest of the extension in JSON encoding.
+ *
+ * See
+ * https://docs.taler.net/design-documents/006-extensions.html#tsref-type-Extension
+ * for the definition.
+ *
+ * @param ext The extension object
+ * @return The JSON encoding of the extension, if enabled, NULL otherwise.
+ */
+ json_t *(*manifest)(
const struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*load_taler_config)(
- struct TALER_Extension *ext,
- const struct GNUNET_CONFIGURATION_Handle *cfg);
+ /* =========================
+ * Policy related handlers
+ * =========================
+ */
+
+ /**
+ * @brief Handler to check an incoming policy and create a
+ * TALER_PolicyDetails. Can be NULL;
+ *
+ * When a deposit request refers to this extension in its policy
+ * (see https://docs.taler.net/core/api-exchange.html#deposit), this handler
+ * will be called before the deposit transaction.
+ *
+ * @param[in] policy_json Details about the policy, provided by the client
+ * during a deposit request.
+ * @param[out] details On success, will contain the details to the policy,
+ * evaluated by the corresponding policy handler.
+ * @param[out] error_hint On error, will contain a hint
+ * @return GNUNET_OK if the data was accepted by the extension.
+ */
+ enum GNUNET_GenericReturnValue (*create_policy_details)(
+ const json_t *policy_json,
+ struct TALER_PolicyDetails *details,
+ const char **error_hint);
+
+ /**
+ * @brief Handler for POST-requests to the /extensions/$name endpoint. Can be NULL.
+ *
+ * @param[in] root The JSON body from the request
+ * @param[in] args Additional query parameters of the request.
+ * @param[in,out] details List of policy details related to the incoming fulfillment proof
+ * @param[in] details_len Size of the list @e details
+ * @param[out] output JSON output to return to the client
+ * @return GNUNET_OK on success.
+ */
+ enum GNUNET_GenericReturnValue (*policy_post_handler)(
+ const json_t *root,
+ const char *const args[],
+ struct TALER_PolicyDetails *details,
+ size_t details_len,
+ json_t **output);
+
+ /**
+ * @brief Handler for GET-requests to the /extensions/$name endpoint. Can be NULL.
+ *
+ * @param connection The current connection
+ * @param root The JSON body from the request
+ * @param args Additional query parameters of the request.
+ * @return MDH result
+ */
+ MHD_RESULT (*policy_get_handler)(
+ struct MHD_Connection *connection,
+ const char *const args[]);
+};
+
+
+/*
+ * @brief simply linked list of extensions
+ */
+
+struct TALER_Extensions
+{
+ struct TALER_Extensions *next;
+ const struct TALER_Extension *extension;
};
/**
@@ -73,70 +220,57 @@ struct TALER_Extension
*/
/*
- * @brief Sets the configuration of the extensions from the given TALER
- * configuration.
+ * @brief Loads the extensions as shared libraries, as specified in the given
+ * TALER configuration.
*
* @param cfg Handle to the TALER configuration
* @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
* or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_load_taler_config (
+TALER_extensions_init (
const struct GNUNET_CONFIGURATION_Handle *cfg);
/*
- * @brief Checks the given obj to be a valid extension object and fill the
- * fields accordingly.
+ * @brief Parses a given JSON object as an extension manifest.
*
- * @param[in] obj Object to verify is a valid extension
+ * @param[in] obj JSON object to parse as an extension manifest
* @param{out] critical will be set to 1 if the extension is critical according to obj
* @param[out] version will be set to the version of the extension according to obj
* @param[out] config will be set to the configuration of the extension according to obj
* @return OK on success, Error otherwise
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_is_json_config (
+TALER_extensions_parse_manifest (
json_t *obj,
int *critical,
const char **version,
json_t **config);
/*
- * @brief Sets the configuration of the extensions from a given JSON object.
+ * @brief Loads extensions according to the manifests.
*
- * The JSON object must be of type ExchangeKeysResponse as described in
- * https://docs.taler.net/design-documents/006-extensions.html#exchange
+ * The JSON object must be of type ExtensionsManifestsResponse as described
+ * in https://docs.taler.net/design-documents/006-extensions.html#exchange
*
- * @param cfg JSON object containing the configuration for all extensions
+ * @param cfg JSON object containing the manifests for all extensions
* @return #GNUNET_OK on success, #GNUNET_SYSERR if unknown extensions were
* found or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_load_json_config (
- json_t *cfg);
+TALER_extensions_load_manifests (
+ json_t *manifests);
/*
* @brief Returns the head of the linked list of extensions.
*/
-const struct TALER_Extension *
+const struct TALER_Extensions *
TALER_extensions_get_head ();
-/*
- * @brief Adds an extension to the linked list of extensions.
- *
- * @param new_extension the new extension to be added
- * @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid
- * (missing fields), GNUNET_NO if there is already an extension with that name
- * or type.
- */
-enum GNUNET_GenericReturnValue
-TALER_extensions_add (
- struct TALER_Extension *new_extension);
-
/**
* @brief Finds and returns a supported extension by a given type.
*
- * @param type type of the extension to lookup
+ * @param type of the extension to lookup
* @return extension found, or NULL (should not happen!)
*/
const struct TALER_Extension *
@@ -154,8 +288,6 @@ const struct TALER_Extension *
TALER_extensions_get_by_name (
const char *name);
-#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
-
/**
* @brief Check if a given type of an extension is enabled
*
@@ -166,12 +298,21 @@ bool
TALER_extensions_is_enabled_type (
enum TALER_Extension_Type type);
+/**
+ * @brief Check if an extension is enabled
+ *
+ * @param extension The extension handler.
+ * @return true enabled, false if not enabled, will assert if type is not found.
+ */
+bool
+TALER_extensions_is_enabled (
+ const struct TALER_Extension *extension);
/*
* Verify the signature of a given JSON object for extensions with the master
* key of the exchange.
*
- * The JSON object must be of type ExchangeKeysResponse as described in
+ * The JSON object must be of type ExtensionsManifestsResponse as described in
* https://docs.taler.net/design-documents/006-extensions.html#exchange
*
* @param extensions JSON object with the extension configuration
@@ -181,14 +322,19 @@ TALER_extensions_is_enabled_type (
* and GNUNET_NO if the signature couldn't be verified.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_verify_json_config_signature (
- json_t *extensions,
+TALER_extensions_verify_manifests_signature (
+ json_t *manifests,
struct TALER_MasterSignatureP *extensions_sig,
struct TALER_MasterPublicKeyP *master_pub);
/*
* TALER Age Restriction Extension
+ *
+ * This extension is special insofar as it directly interacts with coins and
+ * denominations.
+ *
+ * At the same time, it doesn't implement and http- or deposit-handlers.
*/
#define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \
@@ -204,102 +350,39 @@ TALER_extensions_verify_json_config_signature (
| 1 << 21)
#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
-/**
- * @brief Registers the extension for age restriction to the list extensions
- */
-enum GNUNET_GenericReturnValue
-TALER_extension_age_restriction_register ();
-/**
- * @brief Parses a string as a list of age groups.
- *
- * The string must consist of a colon-separated list of increasing integers
- * between 0 and 31. Each entry represents the beginning of a new age group.
- * F.e. the string
- *
- * "8:10:12:14:16:18:21"
- *
- * represents the following list of eight age groups:
- *
- * | Group | Ages |
- * | -----:|:------------- |
- * | 0 | 0, 1, ..., 7 |
- * | 1 | 8, 9 |
- * | 2 | 10, 11 |
- * | 3 | 12, 13 |
- * | 4 | 14, 15 |
- * | 5 | 16, 17 |
- * | 6 | 18, 19, 20 |
- * | 7 | 21, ... |
- *
- * which is then encoded as a bit mask with the corresponding bits set:
- *
- * 31 24 16 8 0
- * | | | | |
- * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
- *
- * @param groups String representation of age groups
- * @param[out] mask Mask representation for age restriction.
- * @return Error, if age groups were invalid, OK otherwise.
+/*
+ * @brief Configuration for Age Restriction
*/
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (
- const char *groups,
- struct TALER_AgeMask *mask);
+struct TALER_AgeRestrictionConfig
+{
+ struct TALER_AgeMask mask;
+ uint8_t num_groups;
+};
-/**
- * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
- *
- * @param mask Age mask
- * @return String representation of the age mask, allocated by GNUNET_malloc.
- * Can be used as value in the TALER config.
- */
-char *
-TALER_age_mask_to_string (
- const struct TALER_AgeMask *mask);
/**
- * @brief Returns true when age restriction is configured and enabled.
+ * @brief Retrieve the age restriction configuration
+ *
+ * @return age restriction configuration if present, otherwise NULL.
*/
-bool
-TALER_extensions_age_restriction_is_enabled ();
+const struct TALER_AgeRestrictionConfig *
+TALER_extensions_get_age_restriction_config ();
/**
- * @brief Returns true when age restriction is configured (might not be
- * _enabled_, though).
+ * @brief Check if age restriction is enabled
+ *
+ * @return true, if age restriction is loaded, configured and enabled; otherwise false.
*/
bool
-TALER_extensions_age_restriction_is_configured ();
-
-/**
- * @brief Returns the currently set age mask. Note that even if age
- * restriction is not enabled, the age mask might be have a non-zero value.
- */
-struct TALER_AgeMask
-TALER_extensions_age_restriction_ageMask ();
-
+TALER_extensions_is_age_restriction_enabled ();
/**
- * @brief Returns the amount of age groups defined. 0 means no age restriction
- * enabled.
- */
-size_t
-TALER_extensions_age_restriction_num_groups ();
-
-/**
- * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
+ * @brief Return the age mask for age restriction
*
- * @param root is the json object
- * @param[out] mask on success, will contain the age mask
- * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
- */
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
- struct TALER_AgeMask *mask);
-
-
-/*
- * TODO: Add Peer2Peer Extension
+ * @return configured age mask, if age restriction is loaded, configured and enabled; otherwise zero mask.
*/
+struct TALER_AgeMask
+TALER_extensions_get_age_restriction_mask ();
#endif
diff --git a/src/include/taler_extensions_policy.h b/src/include/taler_extensions_policy.h
new file mode 100644
index 000000000..14a581f3c
--- /dev/null
+++ b/src/include/taler_extensions_policy.h
@@ -0,0 +1,198 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file include/taler_extensions_policy.h
+ * @brief Interface for policy extensions
+ * @author Özgür Kesim
+ */
+#ifndef TALER_EXTENSIONS_POLICY_H
+#define TALER_EXTENSIONS_POLICY_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_crypto_lib.h"
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+
+/*
+ * @brief Describes the states of fulfillment of a policy bound to a deposit
+ */
+enum TALER_PolicyFulfillmentState
+{
+ /* General error state of an fulfillment. */
+ TALER_PolicyFulfillmentFailure = 0,
+
+ /* The policy is not yet ready due to insufficient funding. More deposits are
+ * necessary for it to become ready . */
+ TALER_PolicyFulfillmentInsufficient = 1,
+
+ /* The policy is funded and ready, pending */
+ TALER_PolicyFulfillmentReady = 2,
+
+ /* Policy is provably fulfilled. */
+ TALER_PolicyFulfillmentSuccess = 3,
+
+ /* Policy fulfillment has timed out */
+ TALER_PolicyFulfillmentTimeout = 4,
+
+ TALER_PolicyFulfillmentStateCount = TALER_PolicyFulfillmentTimeout + 1
+};
+
+
+/*
+ * @brief Returns a string representation of the state of a policy fulfillment
+ */
+const char *
+TALER_policy_fulfillment_state_str (enum TALER_PolicyFulfillmentState state);
+
+
+/* @brief Details of a policy for a deposit request */
+struct TALER_PolicyDetails
+{
+ /* Hash code that should be used for the .policy_hash_code field when
+ * this policy is saved in the policy_details table. */
+ struct GNUNET_HashCode hash_code;
+
+ /* Content of the policy in its original JSON form */
+ json_t *policy_json;
+
+ /* When the deadline is meat and the policy is still in "Ready" state,
+ * a timeout-handler will transfer the amount
+ * (total_amount - policy_fee - refreshable_amount)
+ * to the payto-URI from the corresponding deposit. The value
+ * amount_refreshable will be refreshable by the owner of the
+ * associated deposits's coins */
+ struct GNUNET_TIME_Timestamp deadline;
+
+ /* The amount to which this policy commits to. It must be at least as
+ * large as @e policy_fee. */
+ struct TALER_Amount commitment;
+
+ /* The total sum of contributions from coins so far to fund this
+ * policy. It must be at least as large as @commitment in order to be
+ * sufficiently funded. */
+ struct TALER_Amount accumulated_total;
+
+ /* The fee from the exchange for handling the policy. It is due when
+ * the state changes to Timeout or Success. */
+ struct TALER_Amount policy_fee;
+
+ /* The amount that will be transfered to the payto-URIs from the
+ * corresponding deposits when the fulfillment state changes to Timeout
+ * or Success. Note that a fulfillment handler can alter this upon
+ * arrival of a proof of fulfillment. The remaining amount
+ * (accumulated_amount - policy_amount - transferable_amount) */
+ struct TALER_Amount transferable_amount;
+
+ /* The state of fulfillment of a policy.
+ * - If the state is Insufficient, the client is required to call
+ * /deposit -maybe multiple times- with enough coins and the same
+ * policy details in order to reach the required amount. The state is
+ * then changed to Ready.
+ * - If the state changes to Timeout or Success, a handler will transfer
+ * the amount (total_amount - policy_fee - refreshable_amount) to the
+ * payto-URI from the corresponding deposit. The value
+ * amount_refreshable will be refreshable by the owner of the
+ * associated deposits's coins. */
+ enum TALER_PolicyFulfillmentState fulfillment_state;
+
+ /* If there is a proof of fulfillment, the row ID from the
+ * policy_fulfillment table */
+ uint64_t policy_fulfillment_id;
+ bool no_policy_fulfillment_id;
+};
+
+/*
+ * @brief All information required for the database transaction when handling a
+ * proof of fulfillment request.
+ */
+struct TALER_PolicyFulfillmentTransactionData
+{
+ /* The incoming proof, provided by a client */
+ const json_t *proof;
+
+ /* The Hash of the proof */
+ struct GNUNET_HashCode h_proof;
+
+ /* The timestamp of retrieval of the proof */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /* The ID of the proof in the policy_fulfillment table. Will be set
+ * during the transaction. Needed to fill the table
+ * policy_details_fulfillments. */
+ uint64_t fulfillment_id;
+
+ /* The list of policy details. Will be updated by the policy handler */
+ struct TALER_PolicyDetails *details;
+ size_t details_count;
+};
+
+
+/*
+ * @brief Extracts policy details from the deposit's policy options and the policy extensions
+ *
+ * @param[in] policy_options JSON of the policy options from a deposit request
+ * @param[out] details On GNUNET_OK, the parsed details
+ * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed
+ * @return GNUNET_OK on success, GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was
+ * invalid, with *error_hint maybe non-NULL.
+ */
+enum GNUNET_GenericReturnValue
+TALER_extensions_create_policy_details (
+ const json_t *policy_options,
+ struct TALER_PolicyDetails *details,
+ const char **error_hint);
+
+
+/*
+ * ================================
+ * Merchant refund policy
+ * ================================
+ */
+struct TALER_ExtensionPolicyMerchantRefundPolicyConfig
+{
+ struct GNUNET_TIME_Relative max_timeout;
+};
+
+/*
+ * ================================
+ * Brandt-Vickrey Auctions policy
+ * ================================
+ */
+/*
+ * @brief Configuration for Brandt-Vickrey auctions policy
+ */
+struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig
+{
+ uint16_t max_bidders;
+ uint16_t max_prices;
+ struct TALER_Amount auction_fee;
+};
+
+
+/*
+ * ================================
+ * Escrowed Payments policy
+ * ================================
+ */
+/*
+ * @brief Configuration for escrowed payments policy
+ */
+struct TALER_ExtensionPolicyEscrowedPaymentsConfig
+{
+ struct GNUNET_TIME_Relative max_timeout;
+};
+
+#endif
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 12526e954..d0527cc74 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -765,25 +765,25 @@ TALER_JSON_wire_to_payto (const json_t *wire_s);
/**
- * Hash @a extensions in deposits.
+ * Hash @a policy extensions in deposits.
*
- * @param extensions contract extensions to hash
- * @param[out] ech where to write the extension hash
+ * @param policy contract policy extension to hash
+ * @param[out] ech where to write the policy hash
*/
void
-TALER_deposit_extension_hash (const json_t *extensions,
- struct TALER_ExtensionContractHashP *ech);
+TALER_deposit_policy_hash (const json_t *extensions,
+ struct TALER_ExtensionPolicyHashP *ech);
/**
- * Hash the @a config of an extension, given as JSON
+ * Hash the @a manifests of extensions, given as JSON
*
- * @param config configuration of the extension
- * @param[out] eh where to write the extension hash
+ * @param manifests Manifests of the extensions
+ * @param[out] eh where to write the hash
* @return GNUNET_OK on success, GNUNET_SYSERR on failure
*/
enum GNUNET_GenericReturnValue
-TALER_JSON_extensions_config_hash (const json_t *config,
- struct TALER_ExtensionConfigHashP *eh);
+TALER_JSON_extensions_manifests_hash (const json_t *manifests,
+ struct TALER_ExtensionManifestsHashP *eh);
/**
* Canonicalize a JSON input to a string according to RFC 8785.
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 079f72ed6..1580f3dca 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -492,5 +492,63 @@ char *strchrnul (const char *s, int c);
#endif
+/**
+ * @brief Parses a string as a list of age groups.
+ *
+ * The string must consist of a colon-separated list of increasing integers
+ * between 0 and 31. Each entry represents the beginning of a new age group.
+ * F.e. the string
+ *
+ * "8:10:12:14:16:18:21"
+ *
+ * represents the following list of eight age groups:
+ *
+ * | Group | Ages |
+ * | -----:|:------------- |
+ * | 0 | 0, 1, ..., 7 |
+ * | 1 | 8, 9 |
+ * | 2 | 10, 11 |
+ * | 3 | 12, 13 |
+ * | 4 | 14, 15 |
+ * | 5 | 16, 17 |
+ * | 6 | 18, 19, 20 |
+ * | 7 | 21, ... |
+ *
+ * which is then encoded as a bit mask with the corresponding bits set:
+ *
+ * 31 24 16 8 0
+ * | | | | |
+ * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
+ *
+ * @param groups String representation of age groups
+ * @param[out] mask Mask representation for age restriction.
+ * @return Error, if age groups were invalid, OK otherwise.
+ */
+enum GNUNET_GenericReturnValue
+TALER_parse_age_group_string (
+ const char *groups,
+ struct TALER_AgeMask *mask);
+
+/**
+ * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
+ *
+ * @param mask Age mask
+ * @return String representation of the age mask, allocated by GNUNET_malloc.
+ * Can be used as value in the TALER config.
+ */
+char *
+TALER_age_mask_to_string (
+ const struct TALER_AgeMask *mask);
+
+/**
+ * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
+ *
+ * @param root is the json object
+ * @param[out] mask on success, will contain the age mask
+ * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
+ */
+enum GNUNET_GenericReturnValue
+TALER_JSON_parse_age_groups (const json_t *root,
+ struct TALER_AgeMask *mask);
#endif