exchange_do_purse_merge.sql (5888B)
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_merge( 18 IN in_purse_pub BYTEA, 19 IN in_merge_sig BYTEA, 20 IN in_merge_timestamp INT8, 21 IN in_reserve_sig BYTEA, 22 IN in_partner_url TEXT, 23 IN in_reserve_pub BYTEA, 24 IN in_wallet_h_payto BYTEA, 25 IN in_expiration_date INT8, 26 OUT out_no_partner BOOLEAN, 27 OUT out_no_balance BOOLEAN, 28 OUT out_conflict BOOLEAN) 29 LANGUAGE plpgsql 30 AS $$ 31 DECLARE 32 my_amount taler_amount; 33 DECLARE 34 my_purse_fee taler_amount; 35 DECLARE 36 my_partner_serial_id INT8; 37 DECLARE 38 my_in_reserve_quota BOOLEAN; 39 DECLARE 40 rval RECORD; 41 DECLARE 42 reserve_bal RECORD; 43 DECLARE 44 balance taler_amount; 45 BEGIN 46 47 -- Initialize reserve, if not yet exists. 48 INSERT INTO reserves 49 (reserve_pub 50 ,expiration_date 51 ,gc_date) 52 VALUES 53 (in_reserve_pub 54 ,in_expiration_date 55 ,in_expiration_date) 56 ON CONFLICT DO NOTHING; 57 58 59 IF in_partner_url IS NULL 60 THEN 61 my_partner_serial_id=NULL; 62 ELSE 63 SELECT 64 partner_serial_id 65 INTO 66 my_partner_serial_id 67 FROM partners 68 WHERE partner_base_url=in_partner_url 69 AND start_date <= in_merge_timestamp 70 AND end_date > in_merge_timestamp; 71 IF NOT FOUND 72 THEN 73 out_no_partner=TRUE; 74 out_conflict=FALSE; 75 RETURN; 76 END IF; 77 END IF; 78 79 out_no_partner=FALSE; 80 81 -- Check purse is 'full'. 82 SELECT amount_with_fee 83 ,purse_fee 84 ,in_reserve_quota 85 INTO rval 86 FROM purse_requests pr 87 WHERE purse_pub=in_purse_pub 88 AND (pr.balance).val >= (pr.amount_with_fee).val 89 AND ( (pr.balance).frac >= (pr.amount_with_fee).frac OR 90 (pr.balance).val > (pr.amount_with_fee).val ); 91 IF NOT FOUND 92 THEN 93 out_no_balance=TRUE; 94 out_conflict=FALSE; 95 RETURN; 96 END IF; 97 98 -- We use rval as workaround as we cannot select 99 -- directly into the amount due to Postgres limitations. 100 my_amount := rval.amount_with_fee; 101 my_purse_fee := rval.purse_fee; 102 my_in_reserve_quota := rval.in_reserve_quota; 103 104 out_no_balance=FALSE; 105 106 -- Store purse merge signature, checks for purse_pub uniqueness 107 INSERT INTO purse_merges 108 (partner_serial_id 109 ,reserve_pub 110 ,purse_pub 111 ,merge_sig 112 ,merge_timestamp) 113 VALUES 114 (my_partner_serial_id 115 ,in_reserve_pub 116 ,in_purse_pub 117 ,in_merge_sig 118 ,in_merge_timestamp) 119 ON CONFLICT DO NOTHING; 120 121 IF NOT FOUND 122 THEN 123 -- Idempotency check: see if an identical record exists. 124 -- Note that by checking 'merge_sig', we implicitly check 125 -- identity over everything that the signature covers. 126 PERFORM 127 FROM purse_merges 128 WHERE purse_pub=in_purse_pub 129 AND merge_sig=in_merge_sig; 130 IF NOT FOUND 131 THEN 132 -- Purse was merged, but to some other reserve. Not allowed. 133 out_conflict=TRUE; 134 RETURN; 135 END IF; 136 137 -- "success" 138 out_conflict=FALSE; 139 RETURN; 140 END IF; 141 142 143 -- Remember how this purse was finished. This will conflict 144 -- if the purse was already decided previously. 145 INSERT INTO purse_decision 146 (purse_pub 147 ,action_timestamp 148 ,refunded) 149 VALUES 150 (in_purse_pub 151 ,in_merge_timestamp 152 ,FALSE) 153 ON CONFLICT DO NOTHING; 154 155 IF NOT FOUND 156 THEN 157 -- Purse was already decided (possibly deleted or merged differently). 158 out_conflict=TRUE; 159 RETURN; 160 END IF; 161 162 out_conflict=FALSE; 163 164 165 166 IF (my_in_reserve_quota) 167 THEN 168 UPDATE reserves 169 SET purses_active=purses_active-1 170 WHERE reserve_pub IN 171 (SELECT reserve_pub 172 FROM purse_merges 173 WHERE purse_pub=my_purse_pub 174 LIMIT 1); 175 END IF; 176 177 -- Store account merge signature. 178 INSERT INTO account_merges 179 (reserve_pub 180 ,reserve_sig 181 ,purse_pub 182 ,wallet_h_payto) 183 VALUES 184 (in_reserve_pub 185 ,in_reserve_sig 186 ,in_purse_pub 187 ,in_wallet_h_payto); 188 189 -- If we need a wad transfer, mark purse ready for it. 190 IF (0 != my_partner_serial_id) 191 THEN 192 -- The taler-exchange-router will take care of this. 193 UPDATE purse_actions 194 SET action_date=0 --- "immediately" 195 ,partner_serial_id=my_partner_serial_id 196 WHERE purse_pub=in_purse_pub; 197 ELSE 198 -- This is a local reserve, update reserve balance immediately. 199 200 -- Refund the purse fee, by adding it to the purse value: 201 my_amount.val = my_amount.val + my_purse_fee.val; 202 my_amount.frac = my_amount.frac + my_purse_fee.frac; 203 -- normalize result 204 my_amount.val = my_amount.val + my_amount.frac / 100000000; 205 my_amount.frac = my_amount.frac % 100000000; 206 207 SELECT current_balance 208 INTO reserve_bal 209 FROM reserves 210 WHERE reserve_pub=in_reserve_pub; 211 212 balance = reserve_bal.current_balance; 213 balance.val=balance.val+my_amount.val 214 + CASE 215 WHEN balance.frac + my_amount.frac >= 100000000 216 THEN 1 217 ELSE 0 218 END; 219 balance.frac=balance.frac+my_amount.frac 220 - CASE 221 WHEN balance.frac + my_amount.frac >= 100000000 222 THEN 100000000 223 ELSE 0 224 END; 225 226 UPDATE reserves 227 SET current_balance=balance 228 WHERE reserve_pub=in_reserve_pub; 229 230 END IF; 231 232 RETURN; 233 234 END $$; 235 236 COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, TEXT, BYTEA, BYTEA, INT8) 237 IS 'Checks that the partner exists, the purse has not been merged with a different reserve and that the purse is full. If so, persists the merge data and either merges the purse with the reserve or marks it as ready for the taler-exchange-router. Caller MUST abort the transaction on failures so as to not persist data by accident.';