do_refresh.sql (4895B)
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 noreveal_index 81 INTO out_noreveal_index 82 FROM exchange.refresh 83 WHERE rc=in_rc; 84 out_idempotent = FOUND; 85 86 IF out_idempotent 87 THEN 88 -- out_idempotent is set 89 -- out_noreveal_index is set 90 -- out_coin_found is set 91 -- out_coin_balance is set 92 out_balance_ok = TRUE; 93 out_zombie_bad = FALSE; -- zombie is OK 94 out_nonce_reuse = FALSE; 95 RETURN; 96 END IF; 97 98 out_idempotent = FALSE; 99 out_noreveal_index = in_noreveal_index; 100 101 -- Ensure the uniqueness of the blinding_seed 102 IF in_blinding_seed IS NOT NULL 103 THEN 104 INSERT INTO unique_refresh_blinding_seed 105 (blinding_seed) 106 VALUES 107 (in_blinding_seed) 108 ON CONFLICT DO NOTHING; 109 110 IF NOT FOUND 111 THEN 112 out_nonce_reuse = TRUE; 113 out_balance_ok = TRUE; 114 out_zombie_bad = FALSE; -- zombie is OK 115 RETURN; 116 END IF; 117 END IF; 118 119 out_nonce_reuse = FALSE; 120 121 INSERT INTO exchange.refresh 122 (rc 123 ,execution_date 124 ,old_coin_pub 125 ,old_coin_sig 126 ,planchets_h 127 ,transfer_pubs 128 ,amount_with_fee 129 ,noreveal_index 130 ,refresh_seed 131 ,blinding_seed 132 ,cs_r_values 133 ,cs_r_choices 134 ,selected_h 135 ,denom_sigs 136 ,denom_serials 137 ) 138 VALUES 139 (in_rc 140 ,in_now 141 ,in_old_coin_pub 142 ,in_old_coin_sig 143 ,in_planchets_h 144 ,in_transfer_pubs 145 ,in_amount_with_fee 146 ,in_noreveal_index 147 ,in_refresh_seed 148 ,in_blinding_seed 149 ,in_cs_r_values 150 ,in_cs_r_choices 151 ,in_selected_h 152 ,in_denom_sigs 153 ,in_denom_serials 154 ) 155 ON CONFLICT DO NOTHING; 156 157 IF NOT FOUND 158 THEN 159 RAISE EXCEPTION 'Conflict in refresh despite idempotency check for rc(%)!', rc; 160 RETURN; 161 END IF; 162 163 IF in_zombie_required 164 THEN 165 -- Check if this coin was part of a refresh 166 -- operation that was subsequently involved 167 -- in a recoup operation. We begin by all 168 -- refresh operations our coin was involved 169 -- with, then find all associated reveal 170 -- operations, and then see if any of these 171 -- reveal operations was involved in a recoup. 172 PERFORM 173 FROM recoup_refresh 174 WHERE refresh_id IN 175 (SELECT refresh_id 176 FROM refresh 177 WHERE old_coin_pub=in_old_coin_pub); 178 IF NOT FOUND 179 THEN 180 out_zombie_bad=TRUE; 181 out_balance_ok=FALSE; 182 RETURN; 183 END IF; 184 END IF; 185 186 out_zombie_bad=FALSE; -- zombie is OK 187 188 -- Check coin balance is sufficient. 189 SELECT * 190 INTO difference 191 FROM amount_left_minus_right(out_coin_balance 192 ,in_amount_with_fee); 193 194 out_balance_ok = difference.ok; 195 196 IF NOT out_balance_ok 197 THEN 198 RETURN; 199 END IF; 200 201 out_coin_balance = difference.diff; 202 203 -- Check and update balance of the coin. 204 UPDATE known_coins 205 SET 206 remaining = out_coin_balance 207 WHERE 208 known_coin_id = known_coin.known_coin_id; 209 210 END $$;