mte/include/sql/exchange_do_refresh.sql

206 lines
4.7 KiB
PL/PgSQL

--
-- This file is part of TALER
-- Copyright (C) 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/>
--
-- @author Özgür Kesim
DROP FUNCTION IF EXISTS exchange_do_refresh;
CREATE FUNCTION exchange_do_refresh(
IN in_rc BYTEA,
IN in_now INT8,
IN in_refresh_seed BYTEA,
IN in_planchets_h BYTEA,
IN in_amount_with_fee taler_amount,
IN in_blinding_seed BYTEA,
IN in_cs_r_values BYTEA[],
IN in_cs_r_choices INT8,
IN in_selected_h BYTEA,
IN in_denom_sigs BYTEA[],
IN in_denom_serials INT8[],
IN in_old_coin_pub BYTEA,
IN in_old_coin_sig BYTEA,
IN in_noreveal_index INT4,
IN in_zombie_required BOOLEAN,
OUT out_coin_found BOOLEAN,
OUT out_balance_ok BOOLEAN,
OUT out_zombie_bad BOOLEAN,
OUT out_nonce_reuse BOOLEAN,
OUT out_idempotent BOOLEAN,
OUT out_noreveal_index INT4,
OUT out_coin_balance taler_amount)
LANGUAGE plpgsql
AS $$
DECLARE
known_coin RECORD;
difference RECORD;
BEGIN
-- Shards: INSERT refresh (by rc)
-- (rare:) SELECT refresh (by old_coin_pub) -- crosses shards!
-- (rare:) SELECT refresh_revealed_coins (by refresh_id)
-- (rare:) PERFORM recoup_refresh (by rrc_serial) -- crosses shards!
-- UPDATE known_coins (by coin_pub)
-- First, find old coin
SELECT known_coin_id
,remaining
INTO known_coin
FROM known_coins
WHERE coin_pub = in_old_coin_pub;
IF NOT FOUND
THEN
out_coin_found = FALSE;
out_balance_ok = TRUE;
out_zombie_bad = FALSE;
out_nonce_reuse = FALSE;
out_idempotent = FALSE;
out_noreveal_index = -1 ;
out_coin_balance.val = 0;
out_coin_balance.frac = 0;
RETURN;
END IF;
out_coin_found = TRUE;
out_coin_balance = known_coin.remaining;
-- Next, check for idempotency
SELECT TRUE, noreveal_index
INTO out_idempotent, out_noreveal_index
FROM exchange.refresh
WHERE rc=in_rc;
IF out_idempotent
THEN
-- out_idempotent is set
-- out_noreveal_index is set
-- out_coin_found is set
-- out_coin_balance is set
out_balance_ok = TRUE;
out_zombie_bad = FALSE; -- zombie is OK
out_nonce_reuse = FALSE;
RETURN;
END IF;
out_idempotent = FALSE;
out_noreveal_index = in_noreveal_index;
-- Ensure the uniqueness of the blinding_seed
IF in_blinding_seed IS NOT NULL
THEN
INSERT INTO unique_refresh_blinding_seed
(blinding_seed)
VALUES
(in_blinding_seed)
ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
out_nonce_reuse = TRUE;
out_balance_ok = TRUE;
out_zombie_bad = FALSE; -- zombie is OK
RETURN;
END IF;
END IF;
out_nonce_reuse = FALSE;
INSERT INTO exchange.refresh
(rc
,execution_date
,old_coin_pub
,old_coin_sig
,planchets_h
,amount_with_fee
,noreveal_index
,refresh_seed
,blinding_seed
,cs_r_values
,cs_r_choices
,selected_h
,denom_sigs
,denom_serials
)
VALUES
(in_rc
,in_now
,in_old_coin_pub
,in_old_coin_sig
,in_planchets_h
,in_amount_with_fee
,in_noreveal_index
,in_refresh_seed
,in_blinding_seed
,in_cs_r_values
,in_cs_r_choices
,in_selected_h
,in_denom_sigs
,in_denom_serials
)
ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
RAISE EXCEPTION 'Conflict in refresh despite idempotency check for rc(%)!', rc;
RETURN;
END IF;
IF in_zombie_required
THEN
-- Check if this coin was part of a refresh
-- operation that was subsequently involved
-- in a recoup operation. We begin by all
-- refresh operations our coin was involved
-- with, then find all associated reveal
-- operations, and then see if any of these
-- reveal operations was involved in a recoup.
PERFORM
FROM recoup_refresh
WHERE refresh_id IN
(SELECT refresh_id
FROM refresh
WHERE old_coin_pub=in_old_coin_pub);
IF NOT FOUND
THEN
out_zombie_bad=TRUE;
out_balance_ok=FALSE;
RETURN;
END IF;
END IF;
out_zombie_bad=FALSE; -- zombie is OK
-- Check coin balance is sufficient.
SELECT *
INTO difference
FROM amount_left_minus_right(out_coin_balance
,in_amount_with_fee);
out_balance_ok = difference.ok;
IF NOT out_balance_ok
THEN
RETURN;
END IF;
out_coin_balance = difference.diff;
-- Check and update balance of the coin.
UPDATE known_coins
SET
remaining = out_coin_balance
WHERE
known_coin_id = known_coin.known_coin_id;
END $$;