exchange_do_purse_deposit.sql (6357B)
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 102 -- Debit the coin 103 -- Check and update balance of the coin. 104 UPDATE known_coins kc 105 SET 106 remaining.frac=(kc.remaining).frac-in_amount_with_fee.frac 107 + CASE 108 WHEN (kc.remaining).frac < in_amount_with_fee.frac 109 THEN 100000000 110 ELSE 0 111 END, 112 remaining.val=(kc.remaining).val-in_amount_with_fee.val 113 - CASE 114 WHEN (kc.remaining).frac < in_amount_with_fee.frac 115 THEN 1 116 ELSE 0 117 END 118 WHERE coin_pub=in_coin_pub 119 AND ( ((kc.remaining).val > in_amount_with_fee.val) OR 120 ( ((kc.remaining).frac >= in_amount_with_fee.frac) AND 121 ((kc.remaining).val >= in_amount_with_fee.val) ) ); 122 123 IF NOT FOUND 124 THEN 125 -- Insufficient balance. 126 out_balance_ok=FALSE; 127 out_late=FALSE; 128 out_conflict=FALSE; 129 RETURN; 130 END IF; 131 132 133 -- Credit the purse. 134 UPDATE purse_requests pr 135 SET 136 balance.frac=(pr.balance).frac+in_amount_without_fee.frac 137 - CASE 138 WHEN (pr.balance).frac+in_amount_without_fee.frac >= 100000000 139 THEN 100000000 140 ELSE 0 141 END, 142 balance.val=(pr.balance).val+in_amount_without_fee.val 143 + CASE 144 WHEN (pr.balance).frac+in_amount_without_fee.frac >= 100000000 145 THEN 1 146 ELSE 0 147 END 148 WHERE purse_pub=in_purse_pub; 149 150 out_conflict=FALSE; 151 out_balance_ok=TRUE; 152 153 -- See if we can finish the merge or need to update the trigger time and partner. 154 SELECT COALESCE(partner_serial_id,0) 155 ,reserve_pub 156 INTO psi 157 ,my_reserve_pub 158 FROM purse_merges 159 WHERE purse_pub=in_purse_pub; 160 161 IF NOT FOUND 162 THEN 163 -- Purse was not yet merged. We are done. 164 out_late=FALSE; 165 RETURN; 166 END IF; 167 168 SELECT 169 amount_with_fee 170 ,in_reserve_quota 171 INTO 172 rval 173 FROM exchange.purse_requests preq 174 WHERE (purse_pub=in_purse_pub) 175 AND ( ( ( ((preq.amount_with_fee).val <= (preq.balance).val) 176 AND ((preq.amount_with_fee).frac <= (preq.balance).frac) ) 177 OR ((preq.amount_with_fee).val < (preq.balance).val) ) ); 178 IF NOT FOUND 179 THEN 180 out_late=FALSE; 181 RETURN; 182 END IF; 183 184 -- We use rval as workaround as we cannot select 185 -- directly into the amount due to Postgres limitations. 186 my_amount := rval.amount_with_fee; 187 my_in_reserve_quota := rval.in_reserve_quota; 188 189 -- Remember how this purse was finished. 190 INSERT INTO purse_decision 191 (purse_pub 192 ,action_timestamp 193 ,refunded) 194 VALUES 195 (in_purse_pub 196 ,in_now 197 ,FALSE) 198 ON CONFLICT DO NOTHING; 199 200 IF NOT FOUND 201 THEN 202 -- Purse already decided, likely expired. 203 out_late=TRUE; 204 RETURN; 205 END IF; 206 207 out_late=FALSE; 208 209 IF (my_in_reserve_quota) 210 THEN 211 UPDATE reserves 212 SET purses_active=purses_active-1 213 WHERE reserve_pub IN 214 (SELECT reserve_pub 215 FROM purse_merges 216 WHERE purse_pub=my_purse_pub 217 LIMIT 1); 218 END IF; 219 220 221 IF (0 != psi) 222 THEN 223 -- The taler-exchange-router will take care of this. 224 UPDATE purse_actions 225 SET action_date=0 --- "immediately" 226 ,partner_serial_id=psi 227 WHERE purse_pub=in_purse_pub; 228 ELSE 229 -- This is a local reserve, update balance immediately. 230 INSERT INTO reserves 231 (reserve_pub 232 ,current_balance 233 ,expiration_date 234 ,gc_date) 235 VALUES 236 (my_reserve_pub 237 ,my_amount 238 ,in_reserve_expiration 239 ,in_reserve_expiration) 240 ON CONFLICT DO NOTHING; 241 242 IF NOT FOUND 243 THEN 244 -- Reserve existed, thus UPDATE instead of INSERT. 245 UPDATE reserves 246 SET 247 current_balance.frac=(current_balance).frac+my_amount.frac 248 - CASE 249 WHEN (current_balance).frac + my_amount.frac >= 100000000 250 THEN 100000000 251 ELSE 0 252 END 253 ,current_balance.val=(current_balance).val+my_amount.val 254 + CASE 255 WHEN (current_balance).frac + my_amount.frac >= 100000000 256 THEN 1 257 ELSE 0 258 END 259 ,expiration_date=GREATEST(expiration_date,in_reserve_expiration) 260 ,gc_date=GREATEST(gc_date,in_reserve_expiration) 261 WHERE reserve_pub=my_reserve_pub; 262 END IF; 263 264 END IF; 265 266 267 END $$;