exchange_do_refresh.sql (4886B)
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_transfer_pubs BYTEA[], 25 IN in_planchets_h BYTEA, 26 IN in_amount_with_fee taler_amount, 27 IN in_blinding_seed BYTEA, 28 IN in_cs_r_values BYTEA[], 29 IN in_cs_r_choices INT8, 30 IN in_selected_h BYTEA, 31 IN in_denom_sigs BYTEA[], 32 IN in_denom_serials INT8[], 33 IN in_old_coin_pub BYTEA, 34 IN in_old_coin_sig BYTEA, 35 IN in_noreveal_index INT4, 36 IN in_zombie_required BOOLEAN, 37 OUT out_coin_found BOOLEAN, 38 OUT out_balance_ok BOOLEAN, 39 OUT out_zombie_bad BOOLEAN, 40 OUT out_nonce_reuse BOOLEAN, 41 OUT out_idempotent BOOLEAN, 42 OUT out_noreveal_index INT4, 43 OUT out_coin_balance taler_amount) 44 LANGUAGE plpgsql 45 AS $$ 46 DECLARE 47 known_coin RECORD; 48 difference RECORD; 49 BEGIN 50 -- Shards: INSERT refresh (by rc) 51 -- (rare:) SELECT refresh (by old_coin_pub) -- crosses shards! 52 -- (rare:) SELECT refresh_revealed_coins (by refresh_id) 53 -- (rare:) PERFORM recoup_refresh (by rrc_serial) -- crosses shards! 54 -- UPDATE known_coins (by coin_pub) 55 56 -- First, find old coin 57 SELECT known_coin_id 58 ,remaining 59 INTO known_coin 60 FROM known_coins 61 WHERE coin_pub = in_old_coin_pub; 62 63 IF NOT FOUND 64 THEN 65 out_coin_found = FALSE; 66 out_balance_ok = TRUE; 67 out_zombie_bad = FALSE; 68 out_nonce_reuse = FALSE; 69 out_idempotent = FALSE; 70 out_noreveal_index = -1 ; 71 out_coin_balance.val = 0; 72 out_coin_balance.frac = 0; 73 RETURN; 74 END IF; 75 76 out_coin_found = TRUE; 77 out_coin_balance = known_coin.remaining; 78 79 -- Next, check for idempotency 80 SELECT TRUE, noreveal_index 81 INTO out_idempotent, out_noreveal_index 82 FROM exchange.refresh 83 WHERE rc=in_rc; 84 85 IF out_idempotent 86 THEN 87 -- out_idempotent is set 88 -- out_noreveal_index is set 89 -- out_coin_found is set 90 -- out_coin_balance is set 91 out_balance_ok = TRUE; 92 out_zombie_bad = FALSE; -- zombie is OK 93 out_nonce_reuse = FALSE; 94 RETURN; 95 END IF; 96 97 out_idempotent = FALSE; 98 out_noreveal_index = in_noreveal_index; 99 100 -- Ensure the uniqueness of the blinding_seed 101 IF in_blinding_seed IS NOT NULL 102 THEN 103 INSERT INTO unique_refresh_blinding_seed 104 (blinding_seed) 105 VALUES 106 (in_blinding_seed) 107 ON CONFLICT DO NOTHING; 108 109 IF NOT FOUND 110 THEN 111 out_nonce_reuse = TRUE; 112 out_balance_ok = TRUE; 113 out_zombie_bad = FALSE; -- zombie is OK 114 RETURN; 115 END IF; 116 END IF; 117 118 out_nonce_reuse = FALSE; 119 120 INSERT INTO exchange.refresh 121 (rc 122 ,execution_date 123 ,old_coin_pub 124 ,old_coin_sig 125 ,planchets_h 126 ,transfer_pubs 127 ,amount_with_fee 128 ,noreveal_index 129 ,refresh_seed 130 ,blinding_seed 131 ,cs_r_values 132 ,cs_r_choices 133 ,selected_h 134 ,denom_sigs 135 ,denom_serials 136 ) 137 VALUES 138 (in_rc 139 ,in_now 140 ,in_old_coin_pub 141 ,in_old_coin_sig 142 ,in_planchets_h 143 ,in_transfer_pubs 144 ,in_amount_with_fee 145 ,in_noreveal_index 146 ,in_refresh_seed 147 ,in_blinding_seed 148 ,in_cs_r_values 149 ,in_cs_r_choices 150 ,in_selected_h 151 ,in_denom_sigs 152 ,in_denom_serials 153 ) 154 ON CONFLICT DO NOTHING; 155 156 IF NOT FOUND 157 THEN 158 RAISE EXCEPTION 'Conflict in refresh despite idempotency check for rc(%)!', rc; 159 RETURN; 160 END IF; 161 162 IF in_zombie_required 163 THEN 164 -- Check if this coin was part of a refresh 165 -- operation that was subsequently involved 166 -- in a recoup operation. We begin by all 167 -- refresh operations our coin was involved 168 -- with, then find all associated reveal 169 -- operations, and then see if any of these 170 -- reveal operations was involved in a recoup. 171 PERFORM 172 FROM recoup_refresh 173 WHERE refresh_id IN 174 (SELECT refresh_id 175 FROM refresh 176 WHERE old_coin_pub=in_old_coin_pub); 177 IF NOT FOUND 178 THEN 179 out_zombie_bad=TRUE; 180 out_balance_ok=FALSE; 181 RETURN; 182 END IF; 183 END IF; 184 185 out_zombie_bad=FALSE; -- zombie is OK 186 187 -- Check coin balance is sufficient. 188 SELECT * 189 INTO difference 190 FROM amount_left_minus_right(out_coin_balance 191 ,in_amount_with_fee); 192 193 out_balance_ok = difference.ok; 194 195 IF NOT out_balance_ok 196 THEN 197 RETURN; 198 END IF; 199 200 out_coin_balance = difference.diff; 201 202 -- Check and update balance of the coin. 203 UPDATE known_coins 204 SET 205 remaining = out_coin_balance 206 WHERE 207 known_coin_id = known_coin.known_coin_id; 208 209 END $$;