do_purse_deposit.sql (6645B)
1 -- 2 -- This file is part of TALER 3 -- Copyright (C) 2014--2022 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 17 CREATE OR REPLACE FUNCTION exchange_do_purse_deposit( 18 IN in_partner_id INT8, 19 IN in_purse_pub BYTEA, 20 IN in_amount_with_fee taler_amount, 21 IN in_coin_pub BYTEA, 22 IN in_coin_sig BYTEA, 23 IN in_amount_without_fee taler_amount, 24 IN in_reserve_expiration INT8, 25 IN in_now INT8, 26 OUT out_balance_ok BOOLEAN, 27 OUT out_late BOOLEAN, 28 OUT out_conflict BOOLEAN) 29 LANGUAGE plpgsql 30 AS $$ 31 DECLARE 32 was_merged BOOLEAN; 33 DECLARE 34 psi INT8; -- partner's serial ID (set if merged) 35 DECLARE 36 my_amount taler_amount; -- total in purse 37 DECLARE 38 was_paid BOOLEAN; 39 DECLARE 40 my_in_reserve_quota BOOLEAN; 41 DECLARE 42 my_reserve_pub BYTEA; 43 DECLARE 44 rval RECORD; 45 BEGIN 46 47 -- Store the deposit request. 48 INSERT INTO purse_deposits 49 (partner_serial_id 50 ,purse_pub 51 ,coin_pub 52 ,amount_with_fee 53 ,coin_sig) 54 VALUES 55 (in_partner_id 56 ,in_purse_pub 57 ,in_coin_pub 58 ,in_amount_with_fee 59 ,in_coin_sig) 60 ON CONFLICT DO NOTHING; 61 62 IF NOT FOUND 63 THEN 64 -- Idempotency check: check if coin_sig is the same, 65 -- if so, success, otherwise conflict! 66 67 PERFORM 68 FROM purse_deposits 69 WHERE purse_pub = in_purse_pub 70 AND coin_pub = in_coin_pub 71 AND coin_sig = in_coin_sig; 72 IF NOT FOUND 73 THEN 74 -- Deposit exists, but with differences. Not allowed. 75 out_balance_ok=FALSE; 76 out_late=FALSE; 77 out_conflict=TRUE; 78 RETURN; 79 ELSE 80 -- Deposit exists, do not count for balance. Allow. 81 out_late=FALSE; 82 out_balance_ok=TRUE; 83 out_conflict=FALSE; 84 RETURN; 85 END IF; 86 END IF; 87 88 89 -- Check if purse was deleted, if so, abort and prevent deposit. 90 PERFORM 91 FROM exchange.purse_deletion 92 WHERE purse_pub = in_purse_pub; 93 IF FOUND 94 THEN 95 out_late=TRUE; 96 out_balance_ok=FALSE; 97 out_conflict=FALSE; 98 RETURN; 99 END IF; 100 101 -- Check if purse was already decided (merged and paid, or expired 102 -- and refunded), if so, abort and prevent deposit. 103 PERFORM 104 FROM exchange.purse_decision 105 WHERE purse_pub = in_purse_pub; 106 IF FOUND 107 THEN 108 out_late=TRUE; 109 out_balance_ok=FALSE; 110 out_conflict=FALSE; 111 RETURN; 112 END IF; 113 114 115 -- Debit the coin 116 -- Check and update balance of the coin. 117 UPDATE known_coins kc 118 SET 119 remaining.frac=(kc.remaining).frac-in_amount_with_fee.frac 120 + CASE 121 WHEN (kc.remaining).frac < in_amount_with_fee.frac 122 THEN 100000000 123 ELSE 0 124 END, 125 remaining.val=(kc.remaining).val-in_amount_with_fee.val 126 - CASE 127 WHEN (kc.remaining).frac < in_amount_with_fee.frac 128 THEN 1 129 ELSE 0 130 END 131 WHERE coin_pub=in_coin_pub 132 AND ( ((kc.remaining).val > in_amount_with_fee.val) OR 133 ( ((kc.remaining).frac >= in_amount_with_fee.frac) AND 134 ((kc.remaining).val >= in_amount_with_fee.val) ) ); 135 136 IF NOT FOUND 137 THEN 138 -- Insufficient balance. 139 out_balance_ok=FALSE; 140 out_late=FALSE; 141 out_conflict=FALSE; 142 RETURN; 143 END IF; 144 145 146 -- Credit the purse. 147 UPDATE purse_requests pr 148 SET 149 balance.frac=(pr.balance).frac+in_amount_without_fee.frac 150 - CASE 151 WHEN (pr.balance).frac+in_amount_without_fee.frac >= 100000000 152 THEN 100000000 153 ELSE 0 154 END, 155 balance.val=(pr.balance).val+in_amount_without_fee.val 156 + CASE 157 WHEN (pr.balance).frac+in_amount_without_fee.frac >= 100000000 158 THEN 1 159 ELSE 0 160 END 161 WHERE purse_pub=in_purse_pub; 162 163 out_conflict=FALSE; 164 out_balance_ok=TRUE; 165 166 -- See if we can finish the merge or need to update the trigger time and partner. 167 SELECT COALESCE(partner_serial_id,0) 168 ,reserve_pub 169 INTO psi 170 ,my_reserve_pub 171 FROM purse_merges 172 WHERE purse_pub=in_purse_pub; 173 174 IF NOT FOUND 175 THEN 176 -- Purse was not yet merged. We are done. 177 out_late=FALSE; 178 RETURN; 179 END IF; 180 181 SELECT 182 amount_with_fee 183 ,in_reserve_quota 184 INTO 185 rval 186 FROM exchange.purse_requests preq 187 WHERE (purse_pub=in_purse_pub) 188 AND ( ( ( ((preq.amount_with_fee).val <= (preq.balance).val) 189 AND ((preq.amount_with_fee).frac <= (preq.balance).frac) ) 190 OR ((preq.amount_with_fee).val < (preq.balance).val) ) ); 191 IF NOT FOUND 192 THEN 193 out_late=FALSE; 194 RETURN; 195 END IF; 196 197 -- We use rval as workaround as we cannot select 198 -- directly into the amount due to Postgres limitations. 199 my_amount := rval.amount_with_fee; 200 my_in_reserve_quota := rval.in_reserve_quota; 201 202 -- Remember how this purse was finished. 203 INSERT INTO purse_decision 204 (purse_pub 205 ,action_timestamp 206 ,refunded) 207 VALUES 208 (in_purse_pub 209 ,in_now 210 ,FALSE) 211 ON CONFLICT DO NOTHING; 212 213 IF NOT FOUND 214 THEN 215 -- Purse already decided, likely expired. 216 out_late=TRUE; 217 RETURN; 218 END IF; 219 220 out_late=FALSE; 221 222 IF (my_in_reserve_quota) 223 THEN 224 UPDATE reserves 225 SET purses_active=purses_active-1 226 WHERE reserve_pub IN 227 (SELECT reserve_pub 228 FROM purse_merges 229 WHERE purse_pub=in_purse_pub 230 LIMIT 1); 231 END IF; 232 233 234 IF (0 != psi) 235 THEN 236 -- The taler-exchange-router will take care of this. 237 UPDATE purse_actions 238 SET action_date=0 --- "immediately" 239 ,partner_serial_id=psi 240 WHERE purse_pub=in_purse_pub; 241 ELSE 242 -- This is a local reserve, update balance immediately. 243 INSERT INTO reserves 244 (reserve_pub 245 ,current_balance 246 ,expiration_date 247 ,gc_date) 248 VALUES 249 (my_reserve_pub 250 ,my_amount 251 ,in_reserve_expiration 252 ,in_reserve_expiration) 253 ON CONFLICT DO NOTHING; 254 255 IF NOT FOUND 256 THEN 257 -- Reserve existed, thus UPDATE instead of INSERT. 258 UPDATE reserves 259 SET 260 current_balance.frac=(current_balance).frac+my_amount.frac 261 - CASE 262 WHEN (current_balance).frac + my_amount.frac >= 100000000 263 THEN 100000000 264 ELSE 0 265 END 266 ,current_balance.val=(current_balance).val+my_amount.val 267 + CASE 268 WHEN (current_balance).frac + my_amount.frac >= 100000000 269 THEN 1 270 ELSE 0 271 END 272 ,expiration_date=GREATEST(expiration_date,in_reserve_expiration) 273 ,gc_date=GREATEST(gc_date,in_reserve_expiration) 274 WHERE reserve_pub=my_reserve_pub; 275 END IF; 276 277 END IF; 278 279 280 END $$;