[age-withdraw] age-withdraw commit- and reveal-handlers implemented, 12/n

The handlers for the commit- and reveal-phases of the age-withdraw
HTTP-endpoints are implemented, yet not active.

Still missing:

- support for age-withdraw is missing in lib/.
- tests
This commit is contained in:
Özgür Kesim 2023-06-26 00:01:31 +02:00
parent 70bfe0ed1b
commit ddedf03a81
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
42 changed files with 1901 additions and 1608 deletions

@ -1 +1 @@
Subproject commit 5f377301db4d94c485422ecda277fe850e29504a
Subproject commit 31b74264e62c4a7f4a671033e214c43fa2f304c0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -26,12 +26,14 @@
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include "taler-exchange-httpd.h"
#include "taler_json_lib.h"
#include "taler_kyclogic_lib.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_batch-withdraw.h"
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_keys.h"
#include "taler_util.h"
/**
@ -306,8 +308,10 @@ batch_withdraw_transaction (void *cls,
struct BatchWithdrawContext *wc = cls;
uint64_t ruuid;
enum GNUNET_DB_QueryStatus qs;
bool balance_ok = false;
bool found = false;
bool balance_ok = false;
bool age_ok = false;
uint16_t required_age = 0;
char *kyc_required;
struct TALER_PaytoHashP reserve_h_payto;
@ -471,9 +475,11 @@ batch_withdraw_transaction (void *cls,
wc->now,
wc->reserve_pub,
&wc->batch_total,
/* TODO[oec]: add parameter for maximum age and [out]parameter for required age */
TEH_age_restriction_enabled,
&found,
&balance_ok,
&age_ok,
&required_age,
&ruuid);
if (0 > qs)
{
@ -496,7 +502,20 @@ batch_withdraw_transaction (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* TODO[oec]: add error handling for age restriction requirements */
if (! age_ok)
{
/* We respond with the lowest age in the corresponding age group
* of the required age */
uint16_t lowest_age = TALER_get_lowest_age (
&TEH_age_restriction_config.mask,
required_age);
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TEH_RESPONSE_reply_reserve_age_restriction_required (
connection,
lowest_age);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (! balance_ok)
{
@ -722,10 +741,12 @@ parse_planchets (const struct TEH_RequestContext *rc,
struct PlanchetContext *pc = &wc->planchets[i];
struct TEH_DenominationKey *dk;
dk = TEH_keys_denomination_by_hash2 (ksh,
&pc->collectable.denom_pub_hash,
NULL,
NULL);
dk = TEH_keys_denomination_by_hash_from_state (
ksh,
&pc->collectable.denom_pub_hash,
NULL,
NULL);
if (NULL == dk)
{
if (! check_request_idempotent (wc,

View File

@ -127,10 +127,10 @@ TEH_handler_csr_melt (struct TEH_RequestContext *rc,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
NULL);
}
dk = TEH_keys_denomination_by_hash2 (ksh,
denom_pub_hash,
NULL,
NULL);
dk = TEH_keys_denomination_by_hash_from_state (ksh,
denom_pub_hash,
NULL,
NULL);
if (NULL == dk)
{
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
@ -262,10 +262,10 @@ TEH_handler_csr_withdraw (struct TEH_RequestContext *rc,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
NULL);
}
dk = TEH_keys_denomination_by_hash2 (ksh,
&denom_pub_hash,
NULL,
NULL);
dk = TEH_keys_denomination_by_hash_from_state (ksh,
&denom_pub_hash,
NULL,
NULL);
if (NULL == dk)
{
return TEH_RESPONSE_reply_unknown_denom_pub_hash (

View File

@ -148,7 +148,7 @@ extension_update_event_cb (void *cls,
TEH_age_restriction_enabled = false;
if (NULL != conf)
{
TEH_age_restriction_enabled = true;
TEH_age_restriction_enabled = extension->enabled;
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",

View File

@ -1364,7 +1364,6 @@ denomination_info_cb (
&dk->h_denom_pub.hash,
dk,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@ -1753,7 +1752,7 @@ wallet_threshold_cb (void *cls,
*
* @param[in,out] ksh key state handle we build @a krd for
* @param[in] denom_keys_hash hash over all the denomination keys in @a denoms
* @param last_cpd timestamp to use
* @param last_cherry_pick_date timestamp to use
* @param signkeys list of sign keys to return
* @param recoup list of revoked keys to return
* @param denoms list of denominations to return
@ -1764,7 +1763,7 @@ wallet_threshold_cb (void *cls,
static enum GNUNET_GenericReturnValue
create_krd (struct TEH_KeyStateHandle *ksh,
const struct GNUNET_HashCode *denom_keys_hash,
struct GNUNET_TIME_Timestamp last_cpd,
struct GNUNET_TIME_Timestamp last_cherry_pick_date,
json_t *signkeys,
json_t *recoup,
json_t *denoms,
@ -1778,7 +1777,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
struct TALER_ExchangeSignatureP grouped_exchange_sig;
json_t *keys;
GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time));
GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
last_cherry_pick_date.abs_time));
GNUNET_assert (NULL != signkeys);
GNUNET_assert (NULL != recoup);
GNUNET_assert (NULL != denoms);
@ -1788,7 +1788,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (NULL != TEH_currency);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating /keys at cherry pick date %s\n",
GNUNET_TIME_timestamp2s (last_cpd));
GNUNET_TIME_timestamp2s (last_cherry_pick_date));
/* Sign hash over denomination keys */
{
@ -1799,7 +1799,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
TALER_exchange_online_key_set_sign (
&TEH_keys_exchange_sign2_,
ksh,
last_cpd,
last_cherry_pick_date,
denom_keys_hash,
&exchange_pub,
&exchange_sig)))
@ -1820,7 +1820,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
TALER_exchange_online_key_set_sign (
&TEH_keys_exchange_sign2_,
ksh,
last_cpd,
last_cherry_pick_date,
h_grouped,
&grouped_exchange_pub,
&grouped_exchange_sig)))
@ -1876,7 +1876,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_JSON_pack_array_incref ("global_fees",
ksh->global_fees),
GNUNET_JSON_pack_timestamp ("list_issue_date",
last_cpd),
last_cherry_pick_date),
GNUNET_JSON_pack_data_auto ("eddsa_pub",
&exchange_pub),
GNUNET_JSON_pack_data_auto ("eddsa_sig",
@ -2034,7 +2034,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
etag));
krd.etag = GNUNET_strdup (etag);
}
krd.cherry_pick_date = last_cpd;
krd.cherry_pick_date = last_cherry_pick_date;
GNUNET_array_append (ksh->krd_array,
ksh->krd_array_length,
krd);
@ -2054,14 +2054,17 @@ create_krd (struct TEH_KeyStateHandle *ksh,
static enum GNUNET_GenericReturnValue
finish_keys_response (struct TEH_KeyStateHandle *ksh)
{
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
json_t *recoup;
struct SignKeyCtx sctx;
json_t *denoms = NULL;
json_t *grouped_denominations = NULL;
struct GNUNET_TIME_Timestamp last_cpd;
struct GNUNET_TIME_Timestamp last_cherry_pick_date;
struct GNUNET_CONTAINER_Heap *heap;
struct GNUNET_HashContext *hash_context = NULL;
struct GNUNET_HashCode grouped_hash_xor = {0};
/* Remember if we have any denomination with age restriction */
bool has_age_restricted_denomination = false;
sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys);
@ -2094,7 +2097,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
grouped_denominations = json_array ();
GNUNET_assert (NULL != grouped_denominations);
last_cpd = GNUNET_TIME_UNIT_ZERO_TS;
last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS;
// FIXME: This block contains the implementation of the DEPRECATED
// "denom_pubs" array along with the new grouped "denominations".
@ -2124,13 +2127,13 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
/* heap = min heap, sorted by start time */
while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap)))
{
if (GNUNET_TIME_timestamp_cmp (last_cpd,
if (GNUNET_TIME_timestamp_cmp (last_cherry_pick_date,
!=,
dk->meta.start) &&
(! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)) )
(! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) )
{
/*
* This is not the first entry in the heap (because last_cpd !=
* This is not the first entry in the heap (because last_cherry_pick_date !=
* GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different
* start time. Therefore, we create a new entry in ksh.
*/
@ -2143,7 +2146,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
if (GNUNET_OK !=
create_krd (ksh,
&hc,
last_cpd,
last_cherry_pick_date,
sctx.signkeys,
recoup,
denoms,
@ -2152,21 +2155,17 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
GNUNET_TIME_timestamp2s (last_cpd));
GNUNET_TIME_timestamp2s (last_cherry_pick_date));
GNUNET_CRYPTO_hash_context_abort (hash_context);
/* drain heap before destroying it */
while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap)))
/* intentionally empty */;
GNUNET_CONTAINER_heap_destroy (heap);
json_decref (denoms);
json_decref (grouped_denominations);
json_decref (sctx.signkeys);
json_decref (recoup);
return GNUNET_SYSERR;
goto CLEANUP;
}
}
last_cpd = dk->meta.start;
last_cherry_pick_date = dk->meta.start;
{
json_t *denom;
@ -2264,7 +2263,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
int r = json_object_set_new (group->json,
"age_mask",
json_integer (meta.age_mask.bits));
GNUNET_assert (0 == r);
/* Remember that we have found at least _one_ age restricted denomination */
has_age_restricted_denomination = true;
}
/* Create a new array for the denominations in this group */
@ -2386,7 +2389,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
}
GNUNET_CONTAINER_heap_destroy (heap);
if (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time))
if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time))
{
struct GNUNET_HashCode hc;
@ -2395,7 +2398,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
if (GNUNET_OK !=
create_krd (ksh,
&hc,
last_cpd,
last_cherry_pick_date,
sctx.signkeys,
recoup,
denoms,
@ -2404,14 +2407,25 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
GNUNET_TIME_timestamp2s (last_cpd));
json_decref (denoms);
json_decref (grouped_denominations);
json_decref (sctx.signkeys);
json_decref (recoup);
return GNUNET_SYSERR;
GNUNET_TIME_timestamp2s (last_cherry_pick_date));
goto CLEANUP;
}
ksh->management_only = false;
/* Sanity check: Make sure that age restriction is enabled IFF at least
* one age restricted denomination exist */
if (! has_age_restricted_denomination && TEH_age_restriction_enabled)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Age restriction is enabled, but NO denominations with age restriction found!\n");
goto CLEANUP;
}
else if (has_age_restricted_denomination && ! TEH_age_restriction_enabled)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Age restriction is NOT enabled, but denominations with age restriction found!\n");
goto CLEANUP;
}
}
else
{
@ -2419,11 +2433,15 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
"No denomination keys available. Refusing to generate /keys response.\n");
GNUNET_CRYPTO_hash_context_abort (hash_context);
}
ret = GNUNET_OK;
CLEANUP:
json_decref (grouped_denominations);
json_decref (sctx.signkeys);
json_decref (recoup);
json_decref (denoms);
return GNUNET_OK;
return ret;
}
@ -2733,16 +2751,16 @@ TEH_keys_denomination_by_hash (
return NULL;
}
return TEH_keys_denomination_by_hash2 (ksh,
h_denom_pub,
conn,
mret);
return TEH_keys_denomination_by_hash_from_state (ksh,
h_denom_pub,
conn,
mret);
}
struct TEH_DenominationKey *
TEH_keys_denomination_by_hash2 (
struct TEH_KeyStateHandle *ksh,
TEH_keys_denomination_by_hash_from_state (
const struct TEH_KeyStateHandle *ksh,
const struct TALER_DenominationHashP *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret)

View File

@ -234,8 +234,8 @@ TEH_keys_denomination_by_hash (
* or NULL if @a h_denom_pub could not be found
*/
struct TEH_DenominationKey *
TEH_keys_denomination_by_hash2 (
struct TEH_KeyStateHandle *ksh,
TEH_keys_denomination_by_hash_from_state (
const struct TEH_KeyStateHandle *ksh,
const struct TALER_DenominationHashP *h_denom_pub,
struct MHD_Connection *conn,
MHD_RESULT *mret);

View File

@ -511,7 +511,7 @@ resolve_refreshes_reveal_denominations (
}
}
old_dk = TEH_keys_denomination_by_hash2 (
old_dk = TEH_keys_denomination_by_hash_from_state (
ksh,
&rctx->melt.session.coin.denom_pub_hash,
connection,
@ -536,10 +536,10 @@ resolve_refreshes_reveal_denominations (
-1);
if (GNUNET_OK != res)
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
dks[i] = TEH_keys_denomination_by_hash2 (ksh,
&rrcs[i].h_denom_pub,
connection,
&ret);
dks[i] = TEH_keys_denomination_by_hash_from_state (ksh,
&rrcs[i].h_denom_pub,
connection,
&ret);
if (NULL == dks[i])
return ret;
if ( (TALER_DENOMINATION_CS == dks[i]->denom_pub.cipher) &&

View File

@ -1084,6 +1084,20 @@ TEH_RESPONSE_reply_reserve_insufficient_balance (
}
MHD_RESULT
TEH_RESPONSE_reply_reserve_age_restriction_required (
struct MHD_Connection *connection,
uint16_t maximum_allowed_age)
{
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_BAD_REQUEST,
TALER_JSON_pack_ec (TALER_EC_EXCHANGE_RESERVES_AGE_RESTRICTION_REQUIRED),
GNUNET_JSON_pack_uint64 ("maximum_allowed_age",
maximum_allowed_age));
}
MHD_RESULT
TEH_RESPONSE_reply_purse_created (
struct MHD_Connection *connection,

View File

@ -75,6 +75,20 @@ TEH_RESPONSE_reply_reserve_insufficient_balance (
const struct TALER_Amount *balance_required,
const struct TALER_ReservePublicKeyP *reserve_pub);
/**
* Return error message indicating that a reserve requires age
* restriction to be set during withdraw, that is: the age-withdraw
* protocol MUST be used with commitment to an admissible age.
*
* @param connection connection to the client
* @param maximum_allowed_age the balance required for the operation
* @return MHD result code
*/
MHD_RESULT
TEH_RESPONSE_reply_reserve_age_restriction_required (
struct MHD_Connection *connection,
uint16_t maximum_allowed_age);
/**
* Send information that a KYC check must be

View File

@ -488,10 +488,13 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
GNUNET_JSON_parse_free (spec);
return mret;
}
dk = TEH_keys_denomination_by_hash2 (ksh,
&wc.collectable.denom_pub_hash,
NULL,
NULL);
dk = TEH_keys_denomination_by_hash_from_state (
ksh,
&wc.collectable.denom_pub_hash,
NULL,
NULL);
if (NULL == dk)
{
if (! check_request_idempotent (rc,

View File

@ -31,7 +31,7 @@ BEGIN
',current_balance_frac INT4 NOT NULL DEFAULT(0)'
',purses_active INT8 NOT NULL DEFAULT(0)'
',purses_allowed INT8 NOT NULL DEFAULT(0)'
',max_age INT4 NOT NULL DEFAULT(0)'
',birthdate INT4 NOT NULL DEFAULT(0)'
',expiration_date INT8 NOT NULL'
',gc_date INT8 NOT NULL'
') %s ;'
@ -82,7 +82,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Birthday of the user in days after 1970, or 0 if user is an adult and is not subject to age restrictions'
,'max_age'
,'birthdate'
,table_name
,partition_suffix
);

View File

@ -1,6 +1,6 @@
--
-- This file is part of TALER
-- Copyright (C) 2022 Taler Systems SA
-- Copyright (C) 2023 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
@ -13,26 +13,28 @@
-- 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/>
--
-- @author Özgür Kesim
CREATE FUNCTION create_table_age_withdraw_commitments(
CREATE FUNCTION create_table_age_withdraw(
IN partition_suffix VARCHAR DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
table_name VARCHAR DEFAULT 'age_withdraw';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(age_withdraw_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
'(age_withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
',h_commitment BYTEA NOT NULL CONSTRAINT h_commitment_length CHECK(LENGTH(h_commitment)=64)'
',max_age SMALLINT NOT NULL CONSTRAINT max_age_positive CHECK(max_age>=0)'
',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)'
',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)'
',noreveal_index SMALLINT NOT NULL CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
',denominations_serials INT8[] NOT NULL CONSTRAINT denominations_serial_array_length CHECK(cardinality(denominations_serials)=cardinality(denom_sigs))'
',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denominations_serials))'
',h_blind_evs BYTEA[] NOT NULL CONSTRAINT h_blind_evs_length CHECK(cardinality(h_blind_evs)=cardinality(denomination_serials))'
',denom_serials INT8[] NOT NULL CONSTRAINT denominations_serial_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
') %s ;'
,table_name
,'PARTITION BY HASH (reserve_pub)'
@ -40,7 +42,7 @@ BEGIN
);
PERFORM comment_partitioned_table(
'Commitments made when withdrawing coins with age restriction and the gamma value chosen by the exchange. '
'It also contains the blindly signed coins and related denominations.'
'It also contains the blindly signed coins, their signatures and denominations.'
,table_name
,partition_suffix
);
@ -76,12 +78,18 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Array of references to the denominations'
,'denominations_serials'
,'denom_serials'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Array of signatures over the blinded envelopes'
'Array of the blinded envelopes of the chosen fresh coins, with value as given by the denomination in the corresponding slot in denom_serials'
,'h_blind_evs'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Array of signatures over each blinded envelope'
,'denom_sigs'
,table_name
,partition_suffix
@ -90,14 +98,14 @@ END
$$;
CREATE FUNCTION constrain_table_age_withdraw_commitments(
CREATE FUNCTION constrain_table_age_withdraw(
IN partition_suffix VARCHAR
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
table_name VARCHAR DEFAULT 'age_withdraw';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
@ -111,25 +119,25 @@ BEGIN
);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_age_withdraw_commitment_id_key'
' UNIQUE (age_withdraw_commitment_id);'
' ADD CONSTRAINT ' || table_name || '_age_withdraw_id_key'
' UNIQUE (age_withdraw_id);'
);
END
$$;
CREATE FUNCTION foreign_table_age_withdraw_commitments()
CREATE FUNCTION foreign_table_age_withdraw()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
table_name VARCHAR DEFAULT 'age_withdraw';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
' FOREIGN KEY (reserve_pub)'
' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
' REFERENCES reserves(reserve_pub);' -- ON DELETE CASCADE;'
);
END
$$;
@ -142,6 +150,6 @@ INSERT INTO exchange_tables
,partitioned
,by_range)
VALUES
('age_withdraw_commitments', 'exchange-0003', 'create', TRUE ,FALSE),
('age_withdraw_commitments', 'exchange-0003', 'constrain',TRUE ,FALSE),
('age_withdraw_commitments', 'exchange-0003', 'foreign', TRUE ,FALSE);
('age_withdraw', 'exchange-0003', 'create', TRUE ,FALSE),
('age_withdraw', 'exchange-0003', 'constrain',TRUE ,FALSE),
('age_withdraw', 'exchange-0003', 'foreign', TRUE ,FALSE);

View File

@ -1,125 +0,0 @@
--
-- 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/>
--
CREATE FUNCTION create_table_age_withdraw_revealed_coins(
IN partition_suffix VARCHAR DEFAULT NULL
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(age_withdraw_revealed_coins_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
',h_commitment BYTEA NOT NULL CHECK (LENGTH(h_commitment)=64)'
',coin_index INT4 NOT NULL'
',h_coin_ev BYTEA CHECK (LENGTH(h_coin_ev)=64)'
') %s ;'
,table_name
,'PARTITION BY HASH (h_coin_ev)'
,partition_suffix
);
PERFORM comment_partitioned_table(
'Reveal of proofs of the correct age restriction after the commitment when withdrawing coins with age restriction'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Foreign key reference to the corresponding commitment'
,'h_commitment'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Index of the coin in the age-withdraw request, which is implicitly a batch request'
,'coin_index'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Hash of the envelope of the new coin to be signed (for lookups). The corresponding signatures are stores in age_withdraw_commitments.denom_sigs.'
,'h_coin_ev'
,table_name
,partition_suffix
);
END
$$;
CREATE FUNCTION constrain_table_age_withdraw_revealed_coins(
IN partition_suffix VARCHAR
)
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_age_withdraw_revealed_coins_id_key'
' UNIQUE (age_withdraw_revealed_coins_id);'
);
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_freshcoin_index_and_h_commitment_uniqueness'
' UNIQUE (freshcoin_index, h_commitment);'
);
END
$$;
CREATE FUNCTION foreign_table_age_withdraw_revealed_coins()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
' ADD CONSTRAINT ' || table_name || '_foreign_h_commitment'
' FOREIGN KEY (h_commitment)'
' REFERENCES age_withdraw_commitments (h_commitment) ON DELETE CASCADE;'
);
END
$$;
INSERT INTO exchange_tables
(name
,version
,action
,partitioned
,by_range)
VALUES
('age_withdraw_revealed_coins'
,'exchange-0003'
,'create'
,TRUE
,FALSE),
('age_withdraw_revealed_coins'
,'exchange-0003'
,'constrain'
,TRUE
,FALSE),
('age_withdraw_revealed_coins'
,'exchange-0003'
,'foreign'
,TRUE
,FALSE);

View File

@ -128,7 +128,8 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_drain_kyc_alert.h pg_drain_kyc_alert.c \
pg_reserves_in_insert.h pg_reserves_in_insert.c \
pg_get_withdraw_info.h pg_get_withdraw_info.c \
pg_get_age_withdraw_info.c pg_get_age_withdraw_info.h \
pg_get_age_withdraw.c pg_get_age_withdraw.h \
pg_do_age_withdraw.h pg_do_age_withdraw.c \
pg_batch_ensure_coin_known.h pg_batch_ensure_coin_known.c \
pg_do_batch_withdraw.h pg_do_batch_withdraw.c \
pg_get_policy_details.h pg_get_policy_details.c \

View File

@ -25,8 +25,7 @@ SET search_path TO exchange;
#include "0003-aml_status.sql"
#include "0003-aml_staff.sql"
#include "0003-aml_history.sql"
#include "0003-age_withdraw_commitments.sql"
#include "0003-age_withdraw_reveals.sql"
#include "0003-age_withdraw.sql"
COMMIT;

View File

@ -0,0 +1,181 @@
--
-- This file is part of TALER
-- Copyright (C) 2023 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/>
--
-- @author Özgür Kesim
CREATE OR REPLACE FUNCTION exchange_do_age_withdraw(
IN amount_val INT8,
IN amount_frac INT4,
IN rpub BYTEA,
IN rsig BYTEA,
IN now INT8,
IN min_reserve_gc INT8,
IN h_commitment BYTEA,
IN maximum_age_committed INT2, -- in years ϵ [0,1..)
IN noreveal_index INT2,
IN blinded_evs BYTEA[],
IN denom_serials INT8[],
IN denom_sigs BYTEA[],
OUT reserve_found BOOLEAN,
OUT balance_ok BOOLEAN,
OUT age_ok BOOLEAN,
OUT required_age INT2, -- in years ϵ [0,1..)
OUT conflict BOOLEAN,
OUT ruuid INT8)
LANGUAGE plpgsql
AS $$
DECLARE
reserve_gc INT8;
reserve_val INT8;
reserve_frac INT4;
reserve_birthday INT4;
not_before date;
earliest_date date;
BEGIN
-- Shards: reserves by reserve_pub (SELECT)
-- reserves_out (INSERT, with CONFLICT detection) by wih
-- reserves by reserve_pub (UPDATE)
-- reserves_in by reserve_pub (SELECT)
-- wire_targets by wire_target_h_payto
SELECT
current_balance_val
,current_balance_frac
,gc_date
,birthday
,reserve_uuid
INTO
reserve_val
,reserve_frac
,reserve_gc
,reserve_birthday
,ruuid
FROM exchange.reserves
WHERE reserves.reserve_pub=rpub;
IF NOT FOUND
THEN
-- reserve unknown
reserve_found=FALSE;
balance_ok=FALSE;
age_ok=FALSE;
required_age=0;
conflict=FALSE;
ruuid=2;
RETURN;
END IF;
-- Check age requirements
IF ((maximum_age_committed = 0) OR (reserve_birthday = 0))
THEN
-- No commitment to a non-zero age was provided or the reserve is marked as
-- having no age restriction. We can simply pass.
age_ok = OK;
ELSE
not_before=date '1970-01-01' + reserve_birthday;
earliest_date = current_date - make_interval(maximum_age_committed);
--
-- 1970-01-01 + birthday == not_before now
-- | | |
-- <.......not allowed......>[<.....allowed range......>]
-- | | |
-- ____*_____________________*_________*________________* timeline
-- |
-- earliest_date ==
-- now - maximum_age_committed*year
--
IF (earliest_date < not_before)
THEN
reserve_found = TRUE;
balance_ok = FALSE;
age_ok = FALSE;
required_age = extract(year from age(not_before, current_date)) + 1;
RETURN;
END IF;
END IF;
-- Check reserve balance is sufficient.
IF (reserve_val > amount_val)
THEN
IF (reserve_frac >= amount_frac)
THEN
reserve_val=reserve_val - amount_val;
reserve_frac=reserve_frac - amount_frac;
ELSE
reserve_val=reserve_val - amount_val - 1;
reserve_frac=reserve_frac + 100000000 - amount_frac;
END IF;
ELSE
IF (reserve_val = amount_val) AND (reserve_frac >= amount_frac)
THEN
reserve_val=0;
reserve_frac=reserve_frac - amount_frac;
ELSE
balance_ok=FALSE;
RETURN;
END IF;
END IF;
-- Calculate new expiration dates.
min_reserve_gc=GREATEST(min_reserve_gc,reserve_gc);
-- Update reserve balance.
UPDATE reserves SET
gc_date=min_reserve_gc
,current_balance_val=reserve_val
,current_balance_frac=reserve_frac
WHERE
reserves.reserve_pub=rpub;
reserve_found=TRUE;
balance_ok=TRUE;
-- Write the commitment into the age-withdraw table
INSERT INTO exchange.age_withdraw
(h_commitment
,max_age
,reserve_pub
,reserve_sig
,noreveal_index
,denomination_serials
,h_blind_evs
,denom_sigs)
VALUES
(h_commitment
,maximum_age_committed
,rpub
,rsig
,noreveal_index
,denom_serials
,blinded_evs
,denom_sigs)
ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
-- Signal a conflict so that the caller
-- can fetch the actual data from the DB.
conflict=TRUE;
RETURN;
ELSE
conflict=FALSE;
END IF;
END $$;
COMMENT ON FUNCTION exchange_do_age_withdraw(INT8, INT4, BYTEA, BYTEA, INT8, INT8, BYTEA, INT2, INT2, BYTEA[], INT8[], BYTEA[])
IS 'Checks whether the reserve has sufficient balance for an age-withdraw operation (or the request is repeated and was previously approved) and that age requirements are met. If so updates the database with the result. Includes storing the blinded planchets and denomination signatures, or signaling conflict';

View File

@ -1,6 +1,6 @@
--
-- This file is part of TALER
-- Copyright (C) 2014--2022 Taler Systems SA
-- Copyright (C) 2014--2023 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
@ -13,6 +13,8 @@
-- 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/>
--
-- @author Christian Grothoff
-- @author Özgür Kesim
CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw(
IN amount_val INT8,
@ -20,18 +22,20 @@ CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw(
IN rpub BYTEA,
IN now INT8,
IN min_reserve_gc INT8,
-- TODO[oec]: add [IN] parameter for maximum age and [OUT] parameter for required age
IN do_age_check BOOLEAN,
OUT reserve_found BOOLEAN,
OUT balance_ok BOOLEAN,
OUT ruuid INT8)
OUT age_ok BOOLEAN,
OUT allowed_maximum_age INT4, -- in years
OUT ruuid INT8
LANGUAGE plpgsql
AS $$
DECLARE
reserve_gc INT8;
DECLARE
reserve_val INT8;
DECLARE
reserve_frac INT4;
reserve_birthday INT4;
not_before date;
BEGIN
-- Shards: reserves by reserve_pub (SELECT)
-- reserves_out (INSERT, with CONFLICT detection) by wih
@ -44,12 +48,13 @@ SELECT
current_balance_val
,current_balance_frac
,gc_date
,birthday
,reserve_uuid
-- TODO[oec]: get age requirements
INTO
reserve_val
,reserve_frac
,reserve_gc
,reserve_birthday
,ruuid
FROM exchange.reserves
WHERE reserves.reserve_pub=rpub;
@ -59,11 +64,32 @@ THEN
-- reserve unknown
reserve_found=FALSE;
balance_ok=FALSE;
age_ok=FALSE;
allowed_maximum_age=0;
ruuid=2;
RETURN;
END IF;
-- TODO[oec]: check age requirements
-- Check if age requirements are present
IF ((NOT do_age_check) OR (reserve_birthday = 0))
THEN
age_ok = OK;
required_age = 0;
ELSE
-- Age requirements are formally not met: The exchange is setup to support
-- age restrictions (do_age_check == TRUE) and the reserve has a
-- birthday set (reserve_birthday != 0), but the client called the
-- batch-withdraw endpoint instead of the age-withdraw endpoint, which it
-- should have.
not_before=date '1970-01-01' + reserve_birthday;
allowed_maximum_age = extract(year from age(current_date, not_before));
reserve_found=TRUE;
balance_ok=FALSE;
age_ok = FALSE;
RETURN;
END IF;
-- Check reserve balance is sufficient.
IF (reserve_val > amount_val)
@ -82,7 +108,6 @@ ELSE
reserve_val=0;
reserve_frac=reserve_frac - amount_frac;
ELSE
reserve_found=TRUE;
balance_ok=FALSE;
RETURN;
END IF;
@ -104,9 +129,6 @@ balance_ok=TRUE;
END $$;
-- TODO[oec]: Update comment re: age requirements are implemented
COMMENT ON FUNCTION exchange_do_batch_withdraw(INT8, INT4, BYTEA, INT8, INT8)
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result. Excludes storing the planchets.';
COMMENT ON FUNCTION exchange_do_batch_withdraw(INT8, INT4, BYTEA, INT8, INT8, BOOLEAN)
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and that age requirements are formally met. If so updates the database with the result. Excludes storing the planchets.';

View File

@ -14,14 +14,11 @@
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
-- TODO[oec]: add min. age requirements handling
CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw_insert(
IN cs_nonce BYTEA,
IN amount_val INT8,
IN amount_frac INT4,
IN h_denom_pub BYTEA,
IN h_denom_pub BYTEA, -- FIXME: denom_serials should really be a parameter to this FUNCTION.
IN ruuid INT8,
IN reserve_sig BYTEA,
IN h_coin_envelope BYTEA,
@ -45,6 +42,8 @@ out_denom_unknown=TRUE;
out_conflict=TRUE;
out_nonce_reuse=TRUE;
-- FIXME: denom_serials should really be a parameter to this FUNCTION.
SELECT denominations_serial
INTO denom_serial
FROM exchange.denominations

View File

@ -61,7 +61,7 @@ UPDATE exchange.legitimization_processes
out_ok = FOUND;
-- FIXME-Oec: update exchange reserve table to store in_birthday here!
-- UPDATE exchange.reserves SET max_age=in_birthday WHERE reserve_pub=X;
-- UPDATE exchange.reserves SET birthday=in_birthday WHERE reserve_pub=X;
IF in_require_aml
THEN

View File

@ -0,0 +1,104 @@
/*
This file is part of TALER
Copyright (C) 2023 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 exchangedb/pg_do_batch_withdraw.c
* @brief Implementation of the do_batch_withdraw function for Postgres
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_exchangedb_plugin.h"
#include "taler_pq_lib.h"
#include "taler_pq_lib.h"
#include "pg_do_batch_withdraw.h"
#include "pg_helper.h"
#include <gnunet/gnunet_time_lib.h>
enum GNUNET_DB_QueryStatus
TEH_PG_do_age_withdraw (
void *cls,
const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
struct GNUNET_TIME_Timestamp now,
bool *found,
bool *balance_ok,
bool *age_ok,
uint16_t *required_age,
bool *conflict,
uint64_t *ruuid)
{
struct PostgresClosure *pg = cls;
struct GNUNET_TIME_Timestamp gc;
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (&commitment->amount_with_fee),
GNUNET_PQ_query_param_auto_from_type (&commitment->reserve_pub),
GNUNET_PQ_query_param_auto_from_type (&commitment->reserve_sig),
GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_auto_from_type (&commitment->h_commitment),
GNUNET_PQ_query_param_uint16 (&commitment->max_age),
GNUNET_PQ_query_param_uint16 (&commitment->noreveal_index),
GNUNET_PQ_query_param_array_auto_from_type (commitment->num_coins,
commitment->h_coin_evs,
pg->conn),
GNUNET_PQ_query_param_array_uint64 (commitment->num_coins,
commitment->denom_serials,
pg->conn),
GNUNET_PQ_query_param_array_auto_from_type (commitment->num_coins,
commitment->denom_sigs,
pg->conn),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("reserve_found",
found),
GNUNET_PQ_result_spec_bool ("balance_ok",
balance_ok),
GNUNET_PQ_result_spec_bool ("age_ok",
age_ok),
GNUNET_PQ_result_spec_uint16 ("required_age",
required_age),
GNUNET_PQ_result_spec_bool ("conflict",
conflict),
GNUNET_PQ_result_spec_uint64 ("ruuid",
ruuid),
GNUNET_PQ_result_spec_end
};
gc = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (now.abs_time,
pg->legal_reserve_expiration_time));
/* Used in #postgres_do_age_withdraw() to
update the reserve balance and check its status */
PREPARE (pg,
"call_age_withdraw",
"SELECT "
" reserve_found"
",balance_ok"
",age_ok"
",required_age"
",conflict"
",ruuid"
" FROM exchange_do_batch_withdraw"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"call_age_withdraw",
params,
rs);
}

View File

@ -0,0 +1,55 @@
/*
This file is part of TALER
Copyright (C) 2023 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 exchangedb/pg_do_age_withdraw.h
* @brief implementation of the do_age_withdraw function for Postgres
* @author Özgür Kesim
*/
#ifndef PG_DO_AGE_WITHDRAW_H
#define PG_DO_AGE_WITHDRAW_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
/**
* Perform reserve update as part of an age-withdraw operation, checking for
* sufficient balance and fulfillment of age requirements. Finally persisting
* the withdrawal details.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param commitment the commitment with all parameters
* @param now current time (rounded)
* @param[out] found set to true if the reserve was found
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] age_ok set to true if no age requirements are present on the reserve
* @param[out] required_age if @e age_ok is false, set to the maximum allowed age when withdrawing from this reserve
* @param[out] conflict set to true if there already is an entry in the database for the given pair (h_commitment, reserve_pub)
* @param[out] ruuid set to the reserve's UUID (reserves table row)
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
TEH_PG_do_age_withdraw (
void *cls,
const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
const struct GNUNET_TIME_Timestamp now,
bool *found,
bool *balance_ok,
bool *age_ok,
uint16_t *required_age,
bool *conflict,
uint64_t *ruuid);
#endif

View File

@ -17,6 +17,7 @@
* @file exchangedb/pg_do_batch_withdraw.c
* @brief Implementation of the do_batch_withdraw function for Postgres
* @author Christian Grothoff
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
@ -31,10 +32,12 @@ TEH_PG_do_batch_withdraw (
void *cls,
struct GNUNET_TIME_Timestamp now,
const struct TALER_ReservePublicKeyP *reserve_pub,
/* TODO[oec]: add parameter for maximum age and [out]parameter for required age */
const struct TALER_Amount *amount,
bool do_age_check,
bool *found,
bool *balance_ok,
bool *age_ok,
uint16_t *allowed_maximum_age,
uint64_t *ruuid)
{
struct PostgresClosure *pg = cls;
@ -42,6 +45,7 @@ TEH_PG_do_batch_withdraw (
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_amount (amount),
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_bool (do_age_check),
GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_timestamp (&gc),
GNUNET_PQ_query_param_end
@ -51,6 +55,10 @@ TEH_PG_do_batch_withdraw (
found),
GNUNET_PQ_result_spec_bool ("balance_ok",
balance_ok),
GNUNET_PQ_result_spec_bool ("age_ok",
age_ok),
GNUNET_PQ_result_spec_uint16 ("allowed_maximum_age",
allowed_maximum_age),
GNUNET_PQ_result_spec_uint64 ("ruuid",
ruuid),
GNUNET_PQ_result_spec_end
@ -68,8 +76,9 @@ TEH_PG_do_batch_withdraw (
"SELECT "
" reserve_found"
",balance_ok"
",age_ok"
",required_age"
",ruuid"
/* TODO[oec]: add parameter for maximum age and [out] parameter for required age */
" FROM exchange_do_batch_withdraw"
" ($1,$2,$3,$4,$5);");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,

View File

@ -33,8 +33,11 @@
* @param now current time (rounded)
* @param reserve_pub public key of the reserve to debit
* @param amount total amount to withdraw
* @param age_check_required if true, fail if age requirements are set on the reserve
* @param[out] found set to true if the reserve was found
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] age_ok set to true if no age requirements are present on the reserve
* @param[out] allowed_maximum_age if @e age_ok is false, set to the maximum allowed age when withdrawing from this reserve (client needs to call age-withdraw)
* @param[out] ruuid set to the reserve's UUID (reserves table row)
* @return query execution status
*/
@ -44,8 +47,11 @@ TEH_PG_do_batch_withdraw (
struct GNUNET_TIME_Timestamp now,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount,
bool age_check_required,
bool *found,
bool *balance_ok,
bool *age_ok,
uint16_t *allowed_maximum_age,
uint64_t *ruuid);
#endif

View File

@ -14,24 +14,24 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file exchangedb/pg_get_age_withdraw_info.c
* @brief Implementation of the get_age_withdraw_info function for Postgres
* @file exchangedb/pg_get_age_withdraw.c
* @brief Implementation of the get_age_withdraw function for Postgres
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
#include "pg_get_age_withdraw_info.h"
#include "pg_get_age_withdraw.h"
#include "pg_helper.h"
enum GNUNET_DB_QueryStatus
TEH_PG_get_age_withdraw_info (
TEH_PG_get_age_withdraw (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_AgeWithdrawCommitmentHashP *ach,
struct TALER_EXCHANGEDB_AgeWithdrawCommitment *awc)
struct TALER_EXCHANGEDB_AgeWithdraw *aw)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@ -41,26 +41,44 @@ TEH_PG_get_age_withdraw_info (
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("h_commitment",
&awc->h_commitment),
&aw->h_commitment),
GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
&awc->reserve_sig),
&aw->reserve_sig),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&awc->reserve_pub),
&aw->reserve_pub),
GNUNET_PQ_result_spec_uint16 ("max_age",
&awc->max_age),
&aw->max_age),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&awc->amount_with_fee),
GNUNET_PQ_result_spec_uint32 ("noreveal_index",
&awc->noreveal_index),
&aw->amount_with_fee),
GNUNET_PQ_result_spec_uint16 ("noreveal_index",
&aw->noreveal_index),
GNUNET_PQ_result_spec_array_fixed_size (
pg->conn,
"h_coin_evs",
sizeof(struct TALER_BlindedPlanchet),
&aw->num_coins,
(void **) &aw->h_coin_evs),
GNUNET_PQ_result_spec_array_fixed_size (
pg->conn,
"denom_sigs",
sizeof(struct TALER_DenominationSignature),
NULL,
(void **) &aw->denom_sigs),
GNUNET_PQ_result_spec_array_fixed_size (
pg->conn,
"denom_pub_hashes",
sizeof(struct TALER_DenominationHashP),
NULL,
(void **) &aw->denom_pub_hashes),
GNUNET_PQ_result_spec_end
};
/* Used in #postgres_get_age_withdraw_info() to
locate the response for a /reserve/$RESERVE_PUB/age-withdraw request using
the hash of the blinded message. Used to make sure
/reserve/$RESERVE_PUB/age-withdraw requests are idempotent. */
/* Used in #postgres_get_age_withdraw() to
locate the response for a /reserve/$RESERVE_PUB/age-withdraw request
using the hash of the blinded message. Also needed to ensure
idempotency of /reserve/$RESERVE_PUB/age-withdraw requests. */
PREPARE (pg,
"get_age_withdraw_info",
"get_age_withdraw",
"SELECT"
" h_commitment"
",reserve_sig"
@ -69,10 +87,20 @@ TEH_PG_get_age_withdraw_info (
",amount_with_fee_val"
",amount_with_fee_frac"
",noreveal_index"
" FROM age_withdraw_commitments"
",h_coin_evs"
",denom_sigs"
",ARRAY("
" SELECT denominations.denom_pub_hash FROM ("
" SELECT UNNEST(denomination_serials) AS id,"
" generate_subscripts(denominations_serials, 1) AS nr" /* for order */
" ) AS denoms"
" LEFT JOIN denominations ON denominations.denominations_serial=denoms.id"
") AS denom_pub_hashes"
" FROM age_withdraw"
" WHERE reserve_pub=$1 and h_commitment=$2;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_age_withdraw_info",
"get_age_withdraw",
params,
rs);
}

View File

@ -14,12 +14,12 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file exchangedb/pg_get_age_withdraw_info.h
* @brief implementation of the get_age_withdraw_info function for Postgres
* @file exchangedb/pg_get_age_withdraw.h
* @brief implementation of the get_age_withdraw function for Postgres
* @author Özgür KESIM
*/
#ifndef PG_GET_AGE_WITHDRAW_INFO_H
#define PG_GET_AGE_WITHDRAW_INFO_H
#ifndef PG_GET_AGE_WITHDRAW_H
#define PG_GET_AGE_WITHDRAW_H
#include "taler_util.h"
#include "taler_json_lib.h"
@ -33,13 +33,13 @@
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the reserve for which the age-withdraw request is made
* @param ach hash that uniquely identifies the age-withdraw operation
* @param[out] awc corresponding details of the previous age-withdraw request if an entry was found
* @param[out] aw corresponding details of the previous age-withdraw request if an entry was found
* @return statement execution status
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_age_withdraw_info (
TEH_PG_get_age_withdraw (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_AgeWithdrawCommitmentHashP *ach,
struct TALER_EXCHANGEDB_AgeWithdrawCommitment *awc);
struct TALER_EXCHANGEDB_AgeWithdraw *aw);
#endif

View File

@ -27,7 +27,7 @@
enum GNUNET_DB_QueryStatus
TEH_PG_get_withdraw_info (
TEH_PG_get_withdraw (
void *cls,
const struct TALER_BlindedCoinHashP *bch,
struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)

View File

@ -1,106 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2023 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 exchangedb/pg_insert_age_withdraw_reveal.c
* @brief Implementation of the insert_age_withdraw_reveal function for Postgres
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_error_codes.h"
#include "taler_dbevents.h"
#include "taler_pq_lib.h"
#include "pg_insert_refresh_reveal.h"
#include "pg_helper.h"
enum GNUNET_DB_QueryStatus
TEH_PG_insert_age_withdraw_reveal (
void *cls,
/*TODO:oec*/
)
{
struct PostgresClosure *pg = cls;
if (TALER_CNC_KAPPA != num_tprivs + 1)
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* TODO */
#if 0
PREPARE (pg,
"insert_age_withdraw_revealed_coin",
"INSERT INTO age_withdraw_reveals "
"(h_commitment "
",freshcoin_index "
",denominations_serial "
",h_coin_ev "
",ev_sig"
") SELECT $1, $2, $3, "
" denominations_serial, $5, $6, $7, $8"
" FROM denominations"
" WHERE denom_pub_hash=$4"
" ON CONFLICT DO NOTHING;");
for (uint32_t i = 0; i<num_rrcs; i++)
{
const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&melt_serial_id),
GNUNET_PQ_query_param_uint32 (&i),
GNUNET_PQ_query_param_auto_from_type (&rrc->orig_coin_link_sig),
GNUNET_PQ_query_param_auto_from_type (&rrc->h_denom_pub),
TALER_PQ_query_param_blinded_planchet (&rrc->blinded_planchet),
TALER_PQ_query_param_exchange_withdraw_values (&rrc->exchange_vals),
GNUNET_PQ_query_param_auto_from_type (&rrc->coin_envelope_hash),
TALER_PQ_query_param_blinded_denom_sig (&rrc->coin_sig),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_refresh_revealed_coin",
params);
if (0 > qs)
return qs;
}
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&melt_serial_id),
GNUNET_PQ_query_param_auto_from_type (tp),
GNUNET_PQ_query_param_fixed_size (
tprivs,
num_tprivs * sizeof (struct TALER_TransferPrivateKeyP)),
GNUNET_PQ_query_param_end
};
/* Used in #postgres_insert_refresh_reveal() to store the transfer
keys we learned */
PREPARE (pg,
"insert_refresh_transfer_keys",
"INSERT INTO refresh_transfer_keys "
"(melt_serial_id"
",transfer_pub"
",transfer_privs"
") VALUES ($1, $2, $3)"
" ON CONFLICT DO NOTHING;");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_refresh_transfer_keys",
params);
}
#endif
}

View File

@ -1,43 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2023 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 exchangedb/pg_insert_age_withdraw_reveal.h
* @brief implementation of the insert_age_withdraw_reveal function for Postgres
* @author Özgür Kesim
*/
#ifndef PG_INSERT_AGE_WITHDRAW_REVEAL_H
#define PG_INSERT_AGE_WITHDRAW_REVEAL_H
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
/**
* @brief Store in the database which coin(s) the wallet wanted to create in a
* given age-withdraw operation and all of the other information we learned or
* created in the /age-withdraw/reveal step.
*
* @param cls the @e cls of this struct with the plugin-specific state
* TODO:oec
* @return query status for the transaction
*/
enum GNUNET_DB_QueryStatus
TEH_PG_insert_refresh_reveal (
void *cls,
/* TODO: oec */
);
#endif

View File

@ -2057,38 +2057,37 @@ irbt_cb_table_purse_deletion (struct PostgresClosure *pg,
/**
* Function called with age_withdraw_commitments records to insert into table.
* Function called with age_withdraw records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
irbt_cb_table_age_withdraw_commitments (struct PostgresClosure *pg,
const struct
TALER_EXCHANGEDB_TableData *td)
irbt_cb_table_age_withdraw (struct PostgresClosure *pg,
const struct
TALER_EXCHANGEDB_TableData *td)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (
&td->details.age_withdraw_commitments.h_commitment),
&td->details.age_withdraw.h_commitment),
TALER_PQ_query_param_amount (
&td->details.age_withdraw_commitments.amount_with_fee),
&td->details.age_withdraw.amount_with_fee),
GNUNET_PQ_query_param_uint16 (
&td->details.age_withdraw_commitments.max_age),
&td->details.age_withdraw.max_age),
GNUNET_PQ_query_param_auto_from_type (
&td->details.age_withdraw_commitments.reserve_pub),
&td->details.age_withdraw.reserve_pub),
GNUNET_PQ_query_param_auto_from_type (
&td->details.age_withdraw_commitments.reserve_sig),
&td->details.age_withdraw.reserve_sig),
GNUNET_PQ_query_param_uint32 (
&td->details.age_withdraw_commitments.noreveal_index),
GNUNET_PQ_query_param_absolute_time (
&td->details.age_withdraw_commitments.timestamp),
&td->details.age_withdraw.noreveal_index),
/* TODO: other fields, too! */
GNUNET_PQ_query_param_end
};
PREPARE (pg,
"insert_into_table_age_withdraw_commitments",
"INSERT INTO age_withdraw_commitments"
"insert_into_table_age_withdraw",
"INSERT INTO age_withdraw"
"(age_withdraw_commitment_id"
",h_commitment"
",amount_with_fee_val"
@ -2097,64 +2096,10 @@ irbt_cb_table_age_withdraw_commitments (struct PostgresClosure *pg,
",reserve_pub"
",reserve_sig"
",noreveal_index"
",timestamp"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_age_withdraw_commitments",
params);
}
/**
* Function called with age_withdraw_revealed_coins records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
irbt_cb_table_age_withdraw_revealed_coins (struct PostgresClosure *pg,
const struct
TALER_EXCHANGEDB_TableData *td)
{
struct GNUNET_HashCode h_coin_ev;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (
&td->details.age_withdraw_revealed_coins.h_commitment),
GNUNET_PQ_query_param_uint32 (
&td->details.age_withdraw_revealed_coins.freshcoin_index),
GNUNET_PQ_query_param_uint64 (
&td->details.age_withdraw_revealed_coins.denominations_serial),
GNUNET_PQ_query_param_fixed_size (
td->details.age_withdraw_revealed_coins.coin_ev,
td->details.age_withdraw_revealed_coins.coin_ev_size),
GNUNET_PQ_query_param_auto_from_type (&h_coin_ev),
TALER_PQ_query_param_blinded_denom_sig (
&td->details.age_withdraw_revealed_coins.ev_sig),
GNUNET_PQ_query_param_end
};
PREPARE (pg,
"insert_into_table_age_withdraw_revealed_coins",
"INSERT INTO age_withdraw_revealed_coins"
"(age_withdraw_revealed_coins_id"
",h_commitment"
",freshcoin_index"
",denominations_serial"
",coin_ev"
",h_coin_ev"
",ev_sig"
",ewv"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8);");
GNUNET_CRYPTO_hash (td->details.age_withdraw_revealed_coins.coin_ev,
td->details.age_withdraw_revealed_coins.coin_ev_size,
&h_coin_ev);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_age_withdraw_revealed_coins",
"insert_into_table_age_withdraw",
params);
}
@ -2306,11 +2251,8 @@ TEH_PG_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_PURSE_DELETION:
rh = &irbt_cb_table_purse_deletion;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
rh = &irbt_cb_table_age_withdraw_commitments;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
rh = &irbt_cb_table_age_withdraw_revealed_coins;
case TALER_EXCHANGEDB_RT_AGE_WITHDRAW:
rh = &irbt_cb_table_age_withdraw;
break;
}
if (NULL == rh)

View File

@ -72,6 +72,8 @@ dominations_cb_helper (void *cls,
struct TALER_DenominationHashP h_denom_pub = {0};
bool revoked;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("denominations_serial",
&meta.serial),
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
&master_sig),
GNUNET_PQ_result_spec_bool ("revoked",
@ -145,7 +147,8 @@ TEH_PG_iterate_denominations (void *cls,
PREPARE (pg,
"select_denominations",
"SELECT"
" denominations.master_sig"
" denominations_serial"
",denominations.master_sig"
",denom_revocations_serial_id IS NOT NULL AS revoked"
",valid_from"
",expire_withdraw"

View File

@ -2762,109 +2762,48 @@ lrbt_cb_table_purse_deletion (void *cls,
/**
* Function called with age_withdraw_commitments table entries.
* Function called with age_withdraw 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_age_withdraw_commitments (void *cls,
PGresult *result,
unsigned int num_results)
lrbt_cb_table_age_withdraw (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_WITHDRAW_AGE_COMMITMENTS
.table = TALER_EXCHANGEDB_RT_AGE_WITHDRAW
};
for (unsigned int i = 0; i<num_results; i++)
{
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
"age_withdraw_commitment_id",
"age_withdraw_id",
&td.serial),
GNUNET_PQ_result_spec_auto_from_type (
"h_commitment",
&td.details.age_withdraw_commitments.h_commitment),
&td.details.age_withdraw.h_commitment),
GNUNET_PQ_result_spec_uint16 (
"max_age",
&td.details.age_withdraw_commitments.max_age),
&td.details.age_withdraw.max_age),
TALER_PQ_RESULT_SPEC_AMOUNT (
"amount_with_fee",
&td.details.age_withdraw_commitments.amount_with_fee),
&td.details.age_withdraw.amount_with_fee),
GNUNET_PQ_result_spec_auto_from_type (
"reserve_pub",
&td.details.age_withdraw_commitments.reserve_pub),
&td.details.age_withdraw.reserve_pub),
GNUNET_PQ_result_spec_auto_from_type (
"reserve_sig",
&td.details.age_withdraw_commitments.reserve_sig),
&td.details.age_withdraw.reserve_sig),
GNUNET_PQ_result_spec_uint32 (
"noreveal_index",
&td.details.age_withdraw_commitments.noreveal_index),
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 age_withdraw_revealed_coins 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_age_withdraw_revealed_coins (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupRecordsByTableContext *ctx = cls;
struct TALER_EXCHANGEDB_TableData td = {
.table = TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS
};
for (unsigned int i = 0; i<num_results; i++)
{
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
"age_withdraw_revealed_coins_id",
&td.serial),
GNUNET_PQ_result_spec_auto_from_type (
"h_commitment",
&td.details.age_withdraw_revealed_coins.h_commitment),
GNUNET_PQ_result_spec_uint32 (
"freshcoin_index",
&td.details.age_withdraw_revealed_coins.freshcoin_index),
GNUNET_PQ_result_spec_uint64 (
"denominations_serial",
&td.details.age_withdraw_revealed_coins.denominations_serial),
/* Note: h_coin_ev is recalculated */
GNUNET_PQ_result_spec_variable_size (
"coin_ev",
(void **) &td.details.age_withdraw_revealed_coins.coin_ev,
&td.details.age_withdraw_revealed_coins.coin_ev_size),
TALER_PQ_result_spec_blinded_denom_sig (
"ev_sig",
&td.details.refresh_revealed_coins.ev_sig),
TALER_PQ_result_spec_exchange_withdraw_values (
"ewv",
&td.details.refresh_revealed_coins.ewv),
&td.details.age_withdraw.noreveal_index),
/* TODO[oec]: more fields! */
GNUNET_PQ_result_spec_end
};
@ -3591,10 +3530,10 @@ TEH_PG_lookup_records_by_table (void *cls,
" ORDER BY purse_deletion_serial_id ASC;");
rh = &lrbt_cb_table_purse_deletion;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
XPREPARE ("select_above_serial_by_table_age_withdraw_commitments",
case TALER_EXCHANGEDB_RT_AGE_WITHDRAW:
XPREPARE ("select_above_serial_by_table_age_withdraw",
"SELECT"
" age_withdraw_commitment_id"
" age_withdraw_id"
",h_commitment"
",amount_with_fee_val"
",amount_with_fee_frac"
@ -3602,26 +3541,11 @@ TEH_PG_lookup_records_by_table (void *cls,
",reserve_pub"
",reserve_sig"
",noreveal_index"
" FROM age_withdraw_commitments"
" WHERE age_withdraw_commitment_id > $1"
" ORDER BY age_withdraw_commitment_id ASC;");
rh = &lrbt_cb_table_age_withdraw_commitments;
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
XPREPARE ("select_above_serial_by_table_age_withdraw_revealed_coins",
"SELECT"
" age_withdraw_revealed_coins_serial_id"
",h_commitment"
",freshcoin_index"
",denominations_serial"
",coin_ev"
",h_coin_ev"
",ev_sig"
",ewv"
" FROM age_withdraw_revealed_coins"
" WHERE age_withdraw_revealed_coins_serial_id > $1"
" ORDER BY age_withdraw_revealed_coins_serial_id ASC;");
rh = &lrbt_cb_table_age_withdraw_revealed_coins;
" FROM age_withdraw"
" WHERE age_withdraw_id > $1"
" ORDER BY age_withdraw_id ASC;");
/* TODO[oec]: MORE FIELDS! */
rh = &lrbt_cb_table_age_withdraw;
break;
}
if (NULL == rh)

View File

@ -426,23 +426,14 @@ TEH_PG_lookup_serial_by_table (void *cls,
" LIMIT 1;");
statement = "select_serial_by_table_purse_deletion";
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
XPREPARE ("select_serial_by_table_age_withdraw_commitments",
case TALER_EXCHANGEDB_RT_AGE_WITHDRAW:
XPREPARE ("select_serial_by_table_age_withdraw",
"SELECT"
" age_withdraw_commitment_id AS serial"
" FROM age_withdraw_commitments"
" ORDER BY age_withdraw_commitment_id DESC"
" age_withdraw_id AS serial"
" FROM age_withdraw"
" ORDER BY age_withdraw_id DESC"
" LIMIT 1;");
statement = "select_serial_by_table_age_withdraw_commitments";
break;
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
XPREPARE ("select_serial_by_table_age_withdraw_revealed_coins",
"SELECT"
" age_withdraw_revealed_coins_id AS serial"
" FROM age_withdraw_revealed_coins"
" ORDER BY age_withdraw_revealed_coins_id DESC"
" LIMIT 1;");
statement = "select_serial_by_table_age_withdraw_revealed_coins";
statement = "select_serial_by_table_age_withdraw";
break;
}
if (NULL == statement)

View File

@ -117,8 +117,9 @@
#include "pg_drain_kyc_alert.h"
#include "pg_reserves_in_insert.h"
#include "pg_get_withdraw_info.h"
#include "pg_get_age_withdraw_info.h"
#include "pg_get_age_withdraw.h"
#include "pg_do_batch_withdraw.h"
#include "pg_do_age_withdraw.h"
#include "pg_get_policy_details.h"
#include "pg_persist_policy_details.h"
#include "pg_do_deposit.h"
@ -581,8 +582,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_get_withdraw_info;
plugin->do_batch_withdraw
= &TEH_PG_do_batch_withdraw;
plugin->get_age_withdraw_info
= &TEH_PG_get_age_withdraw_info;
plugin->do_age_withdraw
= &TEH_PG_do_age_withdraw;
plugin->get_age_withdraw
= &TEH_PG_get_age_withdraw;
plugin->get_policy_details
= &TEH_PG_get_policy_details;
plugin->persist_policy_details

View File

@ -160,7 +160,7 @@ BEGIN
',current_balance_frac INT4 NOT NULL DEFAULT(0)'
',purses_active INT8 NOT NULL DEFAULT(0)'
',purses_allowed INT8 NOT NULL DEFAULT(0)'
',max_age INT4 NOT NULL DEFAULT(120)'
',birthdate INT4 NOT NULL DEFAULT(0)' -- 0 means: no age restriction.
',expiration_date INT8 NOT NULL'
',gc_date INT8 NOT NULL'
') %s ;'

View File

@ -3728,7 +3728,8 @@ TALER_wallet_withdraw_verify (
*
* @param h_commitment hash all n*kappa blinded coins in the commitment for the age-withdraw
* @param amount_with_fee amount to debit the reserve for
* @param max_age_group maximum age group that the withdrawn coins must be restricted to
* @param mask the mask that defines the age groups
* @param max_age maximum age from which the age group is derived, that the withdrawn coins must be restricted to.
* @param reserve_priv private key to sign with
* @param[out] reserve_sig resulting signature
*/
@ -3736,7 +3737,8 @@ void
TALER_wallet_age_withdraw_sign (
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
const struct TALER_Amount *amount_with_fee,
uint32_t max_age_group,
const struct TALER_AgeMask *mask,
uint8_t max_age,
const struct TALER_ReservePrivateKeyP *reserve_priv,
struct TALER_ReserveSignatureP *reserve_sig);
@ -3745,7 +3747,8 @@ TALER_wallet_age_withdraw_sign (
*
* @param h_commitment hash all n*kappa blinded coins in the commitment for the age-withdraw
* @param amount_with_fee amount to debit the reserve for
* @param max_age_group maximum age group that the withdrawn coins must be restricted to
* @param mask the mask that defines the age groups
* @param max_age maximum age from which the age group is derived, that the withdrawn coins must be restricted to.
* @param reserve_pub public key of the reserve
* @param reserve_sig resulting signature
* @return #GNUNET_OK if the signature is valid
@ -3754,7 +3757,8 @@ enum GNUNET_GenericReturnValue
TALER_wallet_age_withdraw_verify (
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
const struct TALER_Amount *amount_with_fee,
uint32_t max_age_group,
const struct TALER_AgeMask *mask,
uint8_t max_age,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig);

View File

@ -301,8 +301,7 @@ enum TALER_EXCHANGEDB_ReplicatedTable
TALER_EXCHANGEDB_RT_AML_HISTORY,
TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES,
TALER_EXCHANGEDB_RT_PURSE_DELETION,
TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS,
TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS,
TALER_EXCHANGEDB_RT_AGE_WITHDRAW,
};
@ -773,22 +772,14 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_AgeWithdrawCommitmentHashP h_commitment;
struct TALER_Amount amount_with_fee;
uint16_t max_age;
uint32_t noreveal_index;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_ReserveSignatureP reserve_sig;
uint32_t noreveal_index;
struct GNUNET_TIME_Absolute timestamp;
} age_withdraw_commitments;
struct
{
struct TALER_AgeWithdrawCommitmentHashP h_commitment;
uint32_t freshcoin_index;
uint64_t denominations_serial;
void *coin_ev;
size_t coin_ev_size;
// h_coin_ev omitted, to be recomputed!
struct TALER_BlindedDenominationSignature ev_sig;
} age_withdraw_revealed_coins;
uint64_t num_coins;
uint64_t *denominations_serials;
void *h_blind_evs;
struct TALER_BlindedDenominationSignature denom_sigs;
} age_withdraw;
} details;
@ -948,6 +939,13 @@ struct TALER_EXCHANGEDB_Reserve
*/
struct TALER_EXCHANGEDB_DenominationKeyMetaData
{
/**
* Serial of the denomination key as in the DB.
* Can be used in calls to stored procedures in order to spare
* additional lookups.
*/
uint64_t serial;
/**
* Start time of the validity period for this key.
*/
@ -1182,11 +1180,11 @@ struct TALER_EXCHANGEDB_CollectableBlindcoin
/**
* @brief Information we keep for an age-withdraw commitment
* @brief Information we keep for an age-withdraw request
* to reproduce the /age-withdraw operation if needed, and to have proof
* that a reserve was drained by this amount.
*/
struct TALER_EXCHANGEDB_AgeWithdrawCommitment
struct TALER_EXCHANGEDB_AgeWithdraw
{
/**
* Total amount (with fee) committed to withdraw
@ -1194,7 +1192,7 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment
struct TALER_Amount amount_with_fee;
/**
* Maximum age that the coins are restricted to.
* Maximum age (in years) that the coins are restricted to.
*/
uint16_t max_age;
@ -1208,7 +1206,7 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment
* revealed during cut and choose. This value applies to all n coins in the
* commitment.
*/
uint32_t noreveal_index;
uint16_t noreveal_index;
/**
* Public key of the reserve that was drained.
@ -1223,9 +1221,35 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment
struct TALER_ReserveSignatureP reserve_sig;
/**
* The exchange's signature of the response.
* Number of coins to be withdrawn.
*/
struct TALER_ExchangeSignatureP sig;
size_t num_coins;
/**
* Array of @a num_coins blinded coins. These are the chosen coins
* (according to @a noreveal_index) from the request, which contained
* kappa*num_coins blinded coins.
*/
struct TALER_BlindedCoinHashP *h_coin_evs;
/**
* Array of @a num_coins denomination signatures of the blinded coins @a
* h_coin_evs.
*/
struct TALER_BlindedDenominationSignature *denom_sigs;
/**
* Array of @a num_coins serial id's of the denominations, corresponding to
* the coins in @a h_coin_evs.
*/
uint64_t *denom_serials;
/**
* Array of @a num_coins hashes of the public keys of the denominations
* identified by @e denom_serials. This field is set when calling
* get_age_withdraw
*/
struct TALER_DenominationHashP *denom_pub_hashes;
};
@ -2751,28 +2775,6 @@ struct TALER_EXCHANGEDB_CsRevealFreshCoinData
uint32_t coin_off;
};
/**
* Information about a coin that was revealed to the exchange
* during reveal.
*/
struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin
{
/**
* Hash of the public denomination key of the coin.
*/
struct TALER_DenominationHashP h_denom_pub;
/**
* Signature generated by the exchange over the coin (in blinded format).
*/
struct TALER_BlindedDenominationSignature coin_sig;
/**
* Blinded hash of the new coin
*/
struct TALER_BlindedCoinHashP h_coin_ev;
};
/**
* Generic KYC status for some operation.
@ -3760,6 +3762,15 @@ struct TALER_EXCHANGEDB_Plugin
uint64_t *ruuid);
/**
* FIXME: merge do_batch_withdraw and do_batch_withdraw_insert into one API,
* which takes as input (among others)
* - denom_serial[]
* - blinded_coin_evs[]
* - denom_sigs[]
* The implementation should persist the data as _arrays_ in the DB.
*/
/**
* Perform reserve update as part of a batch withdraw operation, checking
* for sufficient balance. Persisting the withdrawal details is done
@ -3769,8 +3780,11 @@ struct TALER_EXCHANGEDB_Plugin
* @param now current time (rounded)
* @param reserve_pub public key of the reserve to debit
* @param amount total amount to withdraw
* @param do_age_check if set, the batch-withdrawal can only succeed when the reserve has no age restriction (birthday) set.
* @param[out] found set to true if the reserve was found
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] age_ok set to true if no age requirements were defined on the reserve or @e do_age_check was false
* @param[out] allowed_maximum_age when @e age_ok is false, set to the allowed maximum age for withdrawal from the reserve. The client MUST then use the age-withdraw endpoint
* @param[out] ruuid set to the reserve's UUID (reserves table row)
* @return query execution status
*/
@ -3780,8 +3794,11 @@ struct TALER_EXCHANGEDB_Plugin
struct GNUNET_TIME_Timestamp now,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount,
bool do_age_check,
bool *found,
bool *balance_ok,
bool *age_ok,
uint16_t *allowed_maximum_age,
uint64_t *ruuid);
@ -3811,73 +3828,49 @@ struct TALER_EXCHANGEDB_Plugin
bool *nonce_reuse);
/**
* Locate the response for a age-withdraw request under a hash that uniquely
* identifies the age-withdraw operation. Used to ensure idempotency of the
* request.
* Locate the response for a age-withdraw request under a hash of the
* commitment and reserve_pub that uniquely identifies the age-withdraw
* operation. Used to ensure idempotency of the request.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the reserve for which the age-withdraw request is made
* @param ach hash that uniquely identifies the age-withdraw operation
* @param[out] awc corresponding details of the previous age-withdraw request if an entry was found
* @param[out] aw corresponding details of the previous age-withdraw request if an entry was found
* @return statement execution status
*/
enum GNUNET_DB_QueryStatus
(*get_age_withdraw_info)(
(*get_age_withdraw)(
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_AgeWithdrawCommitmentHashP *ach,
struct TALER_EXCHANGEDB_AgeWithdrawCommitment *awc);
struct TALER_EXCHANGEDB_AgeWithdraw *aw);
/**
* Perform an age-withdraw operation, checking for sufficient balance
* and possibly persisting the withdrawal details.
* Perform an age-withdraw operation, checking for sufficient balance and
* fulfillment of age requirements and possibly persisting the withdrawal
* details.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param commitment corresponding commitment for the age-withdraw
* @param[out] found set to true if the reserve was found
* @param[out] balance_ok set to true if the balance was sufficient
* @param[out] age_ok set to true if age requirements were met
* @param[out] allowed_maximum_age if @e age_ok is FALSE, this is set to the allowed maximum age
* @param[out] ruuid set to the reserve's UUID (reserves table row)
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
(*do_age_withdraw)(
void *cls,
const struct TALER_EXCHANGEDB_AgeWithdrawCommitment *commitment,
const struct TALER_EXCHANGEDB_AgeWithdraw *commitment,
struct GNUNET_TIME_Timestamp now,
bool *found,
bool *balance_ok,
bool *age_ok,
uint16_t *allowed_maximum_age,
bool *conflict,
uint64_t *ruuid);
/**
* Store in the database which coin(s) the wallet wanted to withdraw with
* age restriction enabled in a given age-withdraw operation and the relevant
* information we learned or created in the reveal steop
*
* @param cls The `struct PostgresClosure` with the plugin-specific state
* @param h_commitment The hash of the original age-withdraw commitment, which is a key into the age_withdraw_commitments table
* @param num_awrcs Number of coins to generate, size of the @a coin_evs array
* @param awrcs Array of @a num_awrcs information about coins to be created
* @return query execution status
*/
enum GNUNET_DB_QueryStatus
(*insert_age_withdraw_reveal)(
void *cls,
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
uint32_t num_awrcs,
const struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin *awrcs);
/**
* Lookup in the database for the fresh coins with age-restriction that
* we created in the given age-withdraw operation.
*
* TODO: oec
*/
enum GNUNET_DB_QueryStatus
(*get_age_withdraw_reveal)(
void *cls,
uint64_t h_commitment
/* TODO: oec */
);
/**
* Retrieve the details to a policy given by its hash_code
*

View File

@ -558,6 +558,18 @@ char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask);
/*
* @brief returns the age group of a given age for a given age mask
*
* @param mask Age mask
* @param age The given age
* @return age group
*/
uint8_t
TALER_get_age_group (
const struct TALER_AgeMask *mask,
uint8_t age);
/**
* @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
*
@ -570,6 +582,18 @@ TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask);
/* @brief Return the lowest age in the corresponding group for a given age
* according the given age mask.
*
* @param[IN] mask age mask
* @param[IN] age age to check
* @return lowest age in corresponding age group
*/
uint8_t
TALER_get_lowest_age (
const struct TALER_AgeMask *mask,
uint8_t age);
/**
* Handle to an external process that will assist
* with some JSON-to-JSON conversion.

View File

@ -76,7 +76,7 @@ TALER_age_commitment_hash (
* defined by the given mask.
*/
uint8_t
get_age_group (
TALER_get_age_group (
const struct TALER_AgeMask *mask,
uint8_t age)
{
@ -95,6 +95,27 @@ get_age_group (
}
uint8_t
TALER_get_lowest_age (
const struct TALER_AgeMask *mask,
uint8_t age)
{
uint32_t m = mask->bits;
uint8_t group = TALER_get_age_group (mask, age);
uint8_t lowest = 0;
while (group > 0)
{
m = m >> 1;
if (m & 1)
group--;
lowest++;
}
return lowest;
}
#ifdef AGE_RESTRICTION_WITH_ECDSA
/* @brief Helper function to generate a ECDSA private key
*
@ -150,7 +171,7 @@ TALER_age_restriction_commit (
GNUNET_assert (mask->bits & 1); /* first bit must have been set */
num_pub = __builtin_popcount (mask->bits) - 1;
num_priv = get_age_group (mask, age);
num_priv = TALER_get_age_group (mask, age);
GNUNET_assert (31 > num_priv);
GNUNET_assert (num_priv <= num_pub);
@ -335,8 +356,8 @@ TALER_age_commitment_attest (
GNUNET_assert (NULL != attest);
GNUNET_assert (NULL != cp);
group = get_age_group (&cp->commitment.mask,
age);
group = TALER_get_age_group (&cp->commitment.mask,
age);
GNUNET_assert (group < 32);
@ -386,8 +407,8 @@ TALER_age_commitment_verify (
GNUNET_assert (NULL != attest);
GNUNET_assert (NULL != comm);
group = get_age_group (&comm->mask,
age);
group = TALER_get_age_group (&comm->mask,
age);
GNUNET_assert (group < 32);
@ -604,7 +625,7 @@ TALER_age_restriction_from_secret (
GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
num_pub = __builtin_popcount (mask->bits) - 1;
num_priv = get_age_group (mask, max_age);
num_priv = TALER_get_age_group (mask, max_age);
GNUNET_assert (31 > num_priv);
GNUNET_assert (num_priv <= num_pub);

View File

@ -21,11 +21,7 @@
*/
#include "platform.h"
#include "taler_util.h"
extern uint8_t
get_age_group (
const struct TALER_AgeMask *mask,
uint8_t age);
#include <gnunet/gnunet_common.h>
/**
* Encodes the age mask into a string, like "8:10:12:14:16:18:21"
@ -113,10 +109,10 @@ test_groups (void)
for (uint8_t i = 0; i < 32; i++)
{
uint8_t r = get_age_group (&mask, i);
uint8_t r = TALER_get_age_group (&mask, i);
char *m = age_mask_to_string (&mask);
printf ("get_age_group(%s, %2d) = %d vs %d (exp)\n",
printf ("TALER_get_age_group(%s, %2d) = %d vs %d (exp)\n",
m,
i,
r,
@ -133,6 +129,52 @@ test_groups (void)
}
enum GNUNET_GenericReturnValue
test_lowest (void)
{
struct TALER_AgeMask mask = {
.bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21
};
struct { uint8_t age; uint8_t expected; }
test [] = {
{.age = 1, .expected = 0 },
{.age = 2, .expected = 0 },
{.age = 3, .expected = 0 },
{.age = 4, .expected = 0 },
{.age = 5, .expected = 5 },
{.age = 6, .expected = 5 },
{.age = 7, .expected = 5 },
{.age = 8, .expected = 5 },
{.age = 9, .expected = 9 },
{.age = 10, .expected = 9 },
{.age = 11, .expected = 9 },
{.age = 12, .expected = 9 },
{.age = 13, .expected = 13 },
{.age = 14, .expected = 13 },
{.age = 15, .expected = 13 },
{.age = 16, .expected = 13 },
{.age = 17, .expected = 17 },
{.age = 18, .expected = 17 },
{.age = 19, .expected = 17 },
{.age = 20, .expected = 17 },
{.age = 21, .expected = 21 },
{.age = 22, .expected = 21 },
};
for (uint8_t n = 0; n < 21; n++)
{
uint8_t l = TALER_get_lowest_age (&mask, test[n].age);
printf ("lowest[%d] for age %d, expected lowest: %d, got: %d\n",
n, test[n].age, test[n].expected, l);
if (test[n].expected != l)
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
static struct TALER_AgeMask age_mask = {
.bits = 1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21
};
@ -146,7 +188,7 @@ test_attestation (void)
enum GNUNET_GenericReturnValue ret;
struct TALER_AgeCommitmentProof acp[3] = {0};
struct TALER_AgeAttestation at = {0};
uint8_t age_group = get_age_group (&age_mask, age);
uint8_t age_group = TALER_get_age_group (&age_mask, age);
struct GNUNET_HashCode seed;
@ -183,7 +225,7 @@ test_attestation (void)
{
for (uint8_t min = 0; min < 22; min++)
{
uint8_t min_group = get_age_group (&age_mask, min);
uint8_t min_group = TALER_get_age_group (&age_mask, min);
ret = TALER_age_commitment_attest (&acp[i],
min,
@ -259,10 +301,12 @@ main (int argc,
NULL);
if (GNUNET_OK != test_groups ())
return 1;
if (GNUNET_OK != test_lowest ())
return 2;
if (GNUNET_OK != test_attestation ())
{
GNUNET_break (0);
return 2;
return 3;
}
return 0;
}

View File

@ -633,10 +633,15 @@ struct TALER_AgeWithdrawRequestPS
*/
struct TALER_AmountNBO amount_with_fee;
/**
* The mask that defines the age groups
*/
struct TALER_AgeMask mask;
/**
* Maximum age group that the coins are going to be restricted to.
*/
uint32_t max_age_group;
uint8_t max_age_group;
};
@ -646,7 +651,8 @@ void
TALER_wallet_age_withdraw_sign (
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
const struct TALER_Amount *amount_with_fee,
uint32_t max_age_group,
const struct TALER_AgeMask *mask,
uint8_t max_age,
const struct TALER_ReservePrivateKeyP *reserve_priv,
struct TALER_ReserveSignatureP *reserve_sig)
{
@ -654,7 +660,8 @@ TALER_wallet_age_withdraw_sign (
.purpose.size = htonl (sizeof (req)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW),
.h_commitment = *h_commitment,
.max_age_group = max_age_group
.mask = *mask,
.max_age_group = TALER_get_age_group (mask, max_age)
};
TALER_amount_hton (&req.amount_with_fee,
@ -669,7 +676,8 @@ enum GNUNET_GenericReturnValue
TALER_wallet_age_withdraw_verify (
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
const struct TALER_Amount *amount_with_fee,
uint32_t max_age_group,
const struct TALER_AgeMask *mask,
uint8_t max_age,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig)
{
@ -677,7 +685,8 @@ TALER_wallet_age_withdraw_verify (
.purpose.size = htonl (sizeof (awsrd)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW),
.h_commitment = *h_commitment,
.max_age_group = max_age_group
.mask = *mask,
.max_age_group = TALER_get_age_group (mask, max_age)
};
TALER_amount_hton (&awsrd.amount_with_fee,