cp sql/ from GNU Taler exchange
This commit is contained in:
parent
4749bb7c62
commit
11ee418801
132 changed files with 16912 additions and 0 deletions
229
include/sql/exchange_do_withdraw.sql
Normal file
229
include/sql/exchange_do_withdraw.sql
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
--
|
||||
-- This file is part of TALER
|
||||
-- Copyright (C) 2023-2025 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/>
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw;
|
||||
|
||||
CREATE FUNCTION exchange_do_withdraw(
|
||||
IN in_amount_with_fee taler_amount,
|
||||
IN in_reserve_pub BYTEA,
|
||||
IN in_reserve_sig BYTEA,
|
||||
IN in_now INT8,
|
||||
IN in_min_reserve_gc INT8,
|
||||
IN in_planchets_h BYTEA,
|
||||
IN in_maximum_age_committed INT2, -- in years ϵ [0,1..), possibly NULL
|
||||
IN in_noreveal_index INT2, -- possibly NULL (if not age-withdraw)
|
||||
IN in_selected_h BYTEA, -- possibly NULL (if not age-withdraw)
|
||||
IN in_denom_serials INT8[],
|
||||
IN in_denom_sigs BYTEA[],
|
||||
IN in_blinding_seed BYTEA, -- possibly NULL (if no CS denominations)
|
||||
IN in_cs_r_values BYTEA[], -- possibly NULL (if no CS denominations)
|
||||
IN in_cs_r_choices INT8, -- possibly NULL (if no CS denominations)
|
||||
OUT out_reserve_found BOOLEAN,
|
||||
OUT out_balance_ok BOOLEAN,
|
||||
OUT out_reserve_balance taler_amount,
|
||||
OUT out_age_ok BOOLEAN,
|
||||
OUT out_required_age INT2, -- in years ϵ [0,1..)
|
||||
OUT out_reserve_birthday INT4,
|
||||
OUT out_idempotent BOOLEAN,
|
||||
OUT out_noreveal_index INT2, -- possibly NULL (if not age-withdraw)
|
||||
OUT out_nonce_reuse BOOLEAN)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
my_reserve RECORD;
|
||||
my_difference RECORD;
|
||||
my_balance taler_amount;
|
||||
my_not_before DATE;
|
||||
my_earliest_date DATE;
|
||||
BEGIN
|
||||
-- Shards: reserves by reserve_pub (SELECT)
|
||||
-- reserves by reserve_pub (UPDATE)
|
||||
|
||||
-- First, find the reserve
|
||||
SELECT current_balance
|
||||
,birthday
|
||||
,gc_date
|
||||
INTO my_reserve
|
||||
FROM reserves
|
||||
WHERE reserve_pub=in_reserve_pub;
|
||||
out_reserve_found = FOUND;
|
||||
|
||||
IF NOT out_reserve_found
|
||||
THEN
|
||||
out_age_ok = FALSE;
|
||||
out_required_age = -1;
|
||||
out_idempotent = FALSE;
|
||||
out_noreveal_index = -1;
|
||||
out_reserve_balance.val = 0;
|
||||
out_reserve_balance.frac = 0;
|
||||
out_balance_ok = FALSE;
|
||||
out_nonce_reuse = FALSE;
|
||||
out_reserve_birthday = 0;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
out_reserve_balance = my_reserve.current_balance;
|
||||
out_reserve_birthday = my_reserve.birthday;
|
||||
|
||||
-- FIXME-performance: probably better to INSERT and on-conflict check for idempotency...
|
||||
|
||||
-- Next, check for idempotency of the withdraw
|
||||
SELECT noreveal_index
|
||||
INTO out_noreveal_index
|
||||
FROM withdraw
|
||||
WHERE reserve_pub = in_reserve_pub
|
||||
AND planchets_h = in_planchets_h;
|
||||
out_idempotent = FOUND;
|
||||
|
||||
IF out_idempotent
|
||||
THEN
|
||||
-- out_idempotent set, out_noreveal_index possibly set, report.
|
||||
out_balance_ok = TRUE;
|
||||
out_age_ok = TRUE;
|
||||
out_required_age = -1;
|
||||
out_nonce_reuse = FALSE;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
out_noreveal_index = -1;
|
||||
|
||||
-- Check age requirements
|
||||
IF (my_reserve.birthday <> 0)
|
||||
THEN
|
||||
my_not_before=date '1970-01-01' + my_reserve.birthday;
|
||||
my_earliest_date = current_date - make_interval(in_maximum_age_committed);
|
||||
--
|
||||
-- 1970-01-01 + birthday == my_not_before now
|
||||
-- | | |
|
||||
-- <.......not allowed......>[<.....allowed range......>]
|
||||
-- | | |
|
||||
-- ____*_____________________*_________*________________* timeline
|
||||
-- |
|
||||
-- my_earliest_date ==
|
||||
-- now - maximum_age_committed*year
|
||||
--
|
||||
IF ( (in_maximum_age_committed IS NULL) OR
|
||||
(my_earliest_date < my_not_before) )
|
||||
THEN
|
||||
out_required_age = extract(year FROM age(current_date, my_not_before));
|
||||
out_age_ok = FALSE;
|
||||
out_balance_ok = TRUE; -- not really
|
||||
out_nonce_reuse = FALSE; -- not really
|
||||
RETURN;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
out_age_ok = TRUE;
|
||||
out_required_age = 0;
|
||||
|
||||
-- Check reserve balance is sufficient.
|
||||
SELECT *
|
||||
INTO my_difference
|
||||
FROM amount_left_minus_right(out_reserve_balance
|
||||
,in_amount_with_fee);
|
||||
|
||||
out_balance_ok = my_difference.ok;
|
||||
IF NOT out_balance_ok
|
||||
THEN
|
||||
out_nonce_reuse = FALSE; -- not yet determined
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
my_balance = my_difference.diff;
|
||||
|
||||
-- Calculate new expiration dates.
|
||||
in_min_reserve_gc=GREATEST(in_min_reserve_gc,my_reserve.gc_date);
|
||||
|
||||
-- Update reserve balance.
|
||||
UPDATE reserves SET
|
||||
gc_date=in_min_reserve_gc
|
||||
,current_balance=my_balance
|
||||
WHERE
|
||||
reserve_pub=in_reserve_pub;
|
||||
|
||||
-- Ensure the uniqueness of the blinding_seed
|
||||
IF in_blinding_seed IS NOT NULL
|
||||
THEN
|
||||
INSERT INTO unique_withdraw_blinding_seed
|
||||
(blinding_seed)
|
||||
VALUES
|
||||
(in_blinding_seed)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
IF NOT FOUND
|
||||
THEN
|
||||
out_nonce_reuse = TRUE;
|
||||
RETURN;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
out_nonce_reuse = FALSE;
|
||||
|
||||
-- Write the data into the withdraw table
|
||||
INSERT INTO withdraw
|
||||
(planchets_h
|
||||
,execution_date
|
||||
,max_age
|
||||
,amount_with_fee
|
||||
,reserve_pub
|
||||
,reserve_sig
|
||||
,noreveal_index
|
||||
,denom_serials
|
||||
,selected_h
|
||||
,blinding_seed
|
||||
,cs_r_values
|
||||
,cs_r_choices
|
||||
,denom_sigs)
|
||||
VALUES
|
||||
(in_planchets_h
|
||||
,in_now
|
||||
,in_maximum_age_committed
|
||||
,in_amount_with_fee
|
||||
,in_reserve_pub
|
||||
,in_reserve_sig
|
||||
,in_noreveal_index
|
||||
,in_denom_serials
|
||||
,in_selected_h
|
||||
,in_blinding_seed
|
||||
,in_cs_r_values
|
||||
,in_cs_r_choices
|
||||
,in_denom_sigs)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
IF NOT FOUND
|
||||
THEN
|
||||
RAISE EXCEPTION 'Conflict on insert into withdraw despite idempotency check for reserve_pub(%) and planchets_h(%)!',
|
||||
in_reserve_pub,
|
||||
in_planchets_h;
|
||||
END IF;
|
||||
|
||||
END $$;
|
||||
|
||||
COMMENT ON FUNCTION exchange_do_withdraw(
|
||||
taler_amount,
|
||||
BYTEA,
|
||||
BYTEA,
|
||||
INT8,
|
||||
INT8,
|
||||
BYTEA,
|
||||
INT2,
|
||||
INT2,
|
||||
BYTEA,
|
||||
INT8[],
|
||||
BYTEA[],
|
||||
BYTEA,
|
||||
BYTEA[],
|
||||
INT8)
|
||||
IS 'Checks whether the reserve has sufficient balance for an 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 hashes of all blinded planchets, (separately) the hashes of the chosen planchets and denomination signatures, or signaling idempotency (and previous noreveal_index) or nonce reuse';
|
||||
Loading…
Add table
Add a link
Reference in a new issue