exchange_do_refresh.sql (4818B)
1 -- 2 -- This file is part of TALER 3 -- Copyright (C) 2025 Taler Systems SA 4 -- 5 -- TALER is free software; you can redistribute it and/or modify it under the 6 -- terms of the GNU General Public License as published by the Free Software 7 -- Foundation; either version 3, or (at your option) any later version. 8 -- 9 -- TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 -- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 -- A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 -- 13 -- You should have received a copy of the GNU General Public License along with 14 -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 -- 16 -- @author Özgür Kesim 17 18 DROP FUNCTION IF EXISTS exchange_do_refresh; 19 20 CREATE FUNCTION exchange_do_refresh( 21 IN in_rc BYTEA, 22 IN in_now INT8, 23 IN in_refresh_seed BYTEA, 24 IN in_planchets_h BYTEA, 25 IN in_amount_with_fee taler_amount, 26 IN in_blinding_seed BYTEA, 27 IN in_cs_r_values BYTEA[], 28 IN in_cs_r_choices INT8, 29 IN in_selected_h BYTEA, 30 IN in_denom_sigs BYTEA[], 31 IN in_denom_serials INT8[], 32 IN in_old_coin_pub BYTEA, 33 IN in_old_coin_sig BYTEA, 34 IN in_noreveal_index INT4, 35 IN in_zombie_required BOOLEAN, 36 OUT out_coin_found BOOLEAN, 37 OUT out_balance_ok BOOLEAN, 38 OUT out_zombie_bad BOOLEAN, 39 OUT out_nonce_reuse BOOLEAN, 40 OUT out_idempotent BOOLEAN, 41 OUT out_noreveal_index INT4, 42 OUT out_coin_balance taler_amount) 43 LANGUAGE plpgsql 44 AS $$ 45 DECLARE 46 known_coin RECORD; 47 difference RECORD; 48 BEGIN 49 -- Shards: INSERT refresh (by rc) 50 -- (rare:) SELECT refresh (by old_coin_pub) -- crosses shards! 51 -- (rare:) SELECT refresh_revealed_coins (by refresh_id) 52 -- (rare:) PERFORM recoup_refresh (by rrc_serial) -- crosses shards! 53 -- UPDATE known_coins (by coin_pub) 54 55 -- First, find old coin 56 SELECT known_coin_id 57 ,remaining 58 INTO known_coin 59 FROM known_coins 60 WHERE coin_pub = in_old_coin_pub; 61 62 IF NOT FOUND 63 THEN 64 out_coin_found = FALSE; 65 out_balance_ok = TRUE; 66 out_zombie_bad = FALSE; 67 out_nonce_reuse = FALSE; 68 out_idempotent = FALSE; 69 out_noreveal_index = -1 ; 70 out_coin_balance.val = 0; 71 out_coin_balance.frac = 0; 72 RETURN; 73 END IF; 74 75 out_coin_found = TRUE; 76 out_coin_balance = known_coin.remaining; 77 78 -- Next, check for idempotency 79 SELECT TRUE, noreveal_index 80 INTO out_idempotent, out_noreveal_index 81 FROM exchange.refresh 82 WHERE rc=in_rc; 83 84 IF out_idempotent 85 THEN 86 -- out_idempotent is set 87 -- out_noreveal_index is set 88 -- out_coin_found is set 89 -- out_coin_balance is set 90 out_balance_ok = TRUE; 91 out_zombie_bad = FALSE; -- zombie is OK 92 out_nonce_reuse = FALSE; 93 RETURN; 94 END IF; 95 96 out_idempotent = FALSE; 97 out_noreveal_index = in_noreveal_index; 98 99 -- Ensure the uniqueness of the blinding_seed 100 IF in_blinding_seed IS NOT NULL 101 THEN 102 INSERT INTO unique_refresh_blinding_seed 103 (blinding_seed) 104 VALUES 105 (in_blinding_seed) 106 ON CONFLICT DO NOTHING; 107 108 IF NOT FOUND 109 THEN 110 out_nonce_reuse = TRUE; 111 out_balance_ok = TRUE; 112 out_zombie_bad = FALSE; -- zombie is OK 113 RETURN; 114 END IF; 115 END IF; 116 117 out_nonce_reuse = FALSE; 118 119 INSERT INTO exchange.refresh 120 (rc 121 ,execution_date 122 ,old_coin_pub 123 ,old_coin_sig 124 ,planchets_h 125 ,amount_with_fee 126 ,noreveal_index 127 ,refresh_seed 128 ,blinding_seed 129 ,cs_r_values 130 ,cs_r_choices 131 ,selected_h 132 ,denom_sigs 133 ,denom_serials 134 ) 135 VALUES 136 (in_rc 137 ,in_now 138 ,in_old_coin_pub 139 ,in_old_coin_sig 140 ,in_planchets_h 141 ,in_amount_with_fee 142 ,in_noreveal_index 143 ,in_refresh_seed 144 ,in_blinding_seed 145 ,in_cs_r_values 146 ,in_cs_r_choices 147 ,in_selected_h 148 ,in_denom_sigs 149 ,in_denom_serials 150 ) 151 ON CONFLICT DO NOTHING; 152 153 IF NOT FOUND 154 THEN 155 RAISE EXCEPTION 'Conflict in refresh despite idempotency check for rc(%)!', rc; 156 RETURN; 157 END IF; 158 159 IF in_zombie_required 160 THEN 161 -- Check if this coin was part of a refresh 162 -- operation that was subsequently involved 163 -- in a recoup operation. We begin by all 164 -- refresh operations our coin was involved 165 -- with, then find all associated reveal 166 -- operations, and then see if any of these 167 -- reveal operations was involved in a recoup. 168 PERFORM 169 FROM recoup_refresh 170 WHERE refresh_id IN 171 (SELECT refresh_id 172 FROM refresh 173 WHERE old_coin_pub=in_old_coin_pub); 174 IF NOT FOUND 175 THEN 176 out_zombie_bad=TRUE; 177 out_balance_ok=FALSE; 178 RETURN; 179 END IF; 180 END IF; 181 182 out_zombie_bad=FALSE; -- zombie is OK 183 184 -- Check coin balance is sufficient. 185 SELECT * 186 INTO difference 187 FROM amount_left_minus_right(out_coin_balance 188 ,in_amount_with_fee); 189 190 out_balance_ok = difference.ok; 191 192 IF NOT out_balance_ok 193 THEN 194 RETURN; 195 END IF; 196 197 out_coin_balance = difference.diff; 198 199 -- Check and update balance of the coin. 200 UPDATE known_coins 201 SET 202 remaining = out_coin_balance 203 WHERE 204 known_coin_id = known_coin.known_coin_id; 205 206 END $$;