exchange_do_deposit.sql (7911B)
1 -- 2 -- This file is part of TALER 3 -- Copyright (C) 2014--2026 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 DROP FUNCTION IF EXISTS exchange_do_deposit; 18 CREATE FUNCTION exchange_do_deposit( 19 -- For batch_deposits 20 IN in_shard INT8, 21 IN in_merchant_pub BYTEA, 22 IN in_merchant_sig BYTEA, 23 IN in_wallet_timestamp INT8, 24 IN in_exchange_timestamp INT8, 25 IN in_refund_deadline INT8, 26 IN in_wire_deadline INT8, 27 IN in_h_contract_terms BYTEA, 28 IN in_wallet_data_hash BYTEA, -- can be NULL 29 IN in_wire_salt BYTEA, 30 IN in_wire_target_h_payto BYTEA, 31 IN in_h_normalized_payto BYTEA, 32 IN in_policy_details_serial_id INT8, -- can be NULL 33 IN in_policy_blocked BOOLEAN, 34 -- For wire_targets 35 IN in_receiver_wire_account TEXT, 36 -- For coin_deposits 37 IN ina_coin_pub BYTEA[], 38 IN ina_coin_sig BYTEA[], 39 IN ina_amount_with_fee taler_amount[], 40 IN ina_deposit_fee taler_amount[], 41 IN in_total_amount taler_amount, 42 IN in_total_without_fee taler_amount, 43 IN in_is_wallet BOOL, 44 IN in_extra_wire_subject_metadata TEXT, 45 OUT out_exchange_timestamp INT8, 46 OUT out_accumulated_total_without_fee taler_amount, 47 OUT out_insufficient_balance_coin_index INT4, -- index of coin with bad balance, NULL if none 48 OUT out_conflict BOOL 49 ) 50 LANGUAGE plpgsql 51 AS $$ 52 DECLARE 53 wtsi INT8; -- wire target serial id 54 bdsi INT8; -- batch_deposits serial id 55 i INT4; 56 my_record RECORD; 57 my_diff RECORD; 58 ini_amount_with_fee taler_amount; 59 ini_deposit_fee taler_amount; 60 ini_coin_pub BYTEA; 61 ini_coin_sig BYTEA; 62 my_update BOOL := FALSE; 63 my_total taler_amount; 64 my_total_without_fee taler_amount; 65 BEGIN 66 -- Shards: 67 -- INSERT wire_targets (by h_payto), ON CONFLICT DO NOTHING; 68 -- INSERT batch_deposits (by shard, merchant_pub), ON CONFLICT idempotency check; 69 -- INSERT[] coin_deposits (by coin_pub), ON CONFLICT idempotency check; 70 -- UPDATE[] known_coins (by coin_pub) 71 72 73 -- Make sure the kyc_target entry exists 74 INSERT INTO kyc_targets 75 (h_normalized_payto 76 ,is_wallet 77 ) VALUES ( 78 in_h_normalized_payto 79 ,in_is_wallet 80 ) 81 ON CONFLICT DO NOTHING; 82 83 -- First, get or create the 'wtsi' 84 INSERT INTO wire_targets 85 (wire_target_h_payto 86 ,h_normalized_payto 87 ,payto_uri 88 ) VALUES ( 89 in_wire_target_h_payto 90 ,in_h_normalized_payto 91 ,in_receiver_wire_account 92 ) 93 ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto) 94 RETURNING wire_target_serial_id 95 INTO wtsi; 96 97 IF NOT FOUND 98 THEN 99 SELECT wire_target_serial_id 100 INTO wtsi 101 FROM wire_targets 102 WHERE wire_target_h_payto=in_wire_target_h_payto; 103 END IF; 104 105 106 -- Second, create the batch_deposits entry 107 INSERT INTO batch_deposits 108 (shard 109 ,merchant_pub 110 ,merchant_sig 111 ,wallet_timestamp 112 ,exchange_timestamp 113 ,refund_deadline 114 ,wire_deadline 115 ,h_contract_terms 116 ,wallet_data_hash 117 ,wire_salt 118 ,wire_target_h_payto 119 ,policy_details_serial_id 120 ,policy_blocked 121 ,total_amount 122 ,total_without_fee 123 ,extra_wire_subject_metadata 124 ) VALUES ( 125 in_shard 126 ,in_merchant_pub 127 ,in_merchant_sig 128 ,in_wallet_timestamp 129 ,in_exchange_timestamp 130 ,in_refund_deadline 131 ,in_wire_deadline 132 ,in_h_contract_terms 133 ,in_wallet_data_hash 134 ,in_wire_salt 135 ,in_wire_target_h_payto 136 ,in_policy_details_serial_id 137 ,in_policy_blocked 138 ,in_total_amount 139 ,in_total_without_fee 140 ,in_extra_wire_subject_metadata) 141 ON CONFLICT DO NOTHING -- for CONFLICT ON (merchant_pub, h_contract_terms) 142 RETURNING 143 batch_deposit_serial_id 144 INTO 145 bdsi; 146 147 IF FOUND 148 THEN 149 -- First batch deposit, we can skip all the 'my_update' logic! 150 out_accumulated_total_without_fee = in_total_without_fee; 151 ELSE 152 -- Idempotency check: see if an identical record exists. 153 -- We do select over merchant_pub, h_contract_terms and wire_target_h_payto 154 -- first to maximally increase the chance of using the existing index. 155 SELECT 156 exchange_timestamp 157 ,batch_deposit_serial_id 158 ,total_amount 159 ,total_without_fee 160 INTO 161 my_record 162 FROM batch_deposits 163 WHERE shard=in_shard 164 AND merchant_pub=in_merchant_pub 165 AND h_contract_terms=in_h_contract_terms 166 AND wire_target_h_payto=in_wire_target_h_payto 167 -- now check the rest, too 168 AND ( (wallet_data_hash=in_wallet_data_hash) OR 169 (wallet_data_hash IS NULL AND in_wallet_data_hash IS NULL) ) 170 AND wire_salt=in_wire_salt 171 AND wallet_timestamp=in_wallet_timestamp 172 AND refund_deadline=in_refund_deadline 173 AND wire_deadline=in_wire_deadline 174 AND ( (policy_details_serial_id=in_policy_details_serial_id) OR 175 (policy_details_serial_id IS NULL AND in_policy_details_serial_id IS NULL) ); 176 IF NOT FOUND 177 THEN 178 -- Deposit exists, but with *strange* differences. Not allowed. 179 out_conflict=TRUE; 180 RETURN; 181 END IF; 182 out_exchange_timestamp = my_record.exchange_timestamp; 183 bdsi = my_record.batch_deposit_serial_id; 184 my_total = my_record.total_amount; 185 my_total_without_fee = my_record.total_without_fee; 186 my_update = TRUE; 187 END IF; 188 189 out_conflict=FALSE; 190 191 -- Deposit each coin 192 193 FOR i IN 1..array_length(ina_coin_pub,1) 194 LOOP 195 ini_coin_pub = ina_coin_pub[i]; 196 ini_coin_sig = ina_coin_sig[i]; 197 ini_amount_with_fee = ina_amount_with_fee[i]; 198 ini_deposit_fee = ina_deposit_fee[i]; 199 200 INSERT INTO coin_deposits 201 (batch_deposit_serial_id 202 ,coin_pub 203 ,coin_sig 204 ,amount_with_fee 205 ) VALUES ( 206 bdsi 207 ,ini_coin_pub 208 ,ini_coin_sig 209 ,ini_amount_with_fee 210 ) 211 ON CONFLICT DO NOTHING; 212 213 IF FOUND 214 THEN 215 -- Insert did happen, update balance in known_coins! 216 UPDATE known_coins kc 217 SET 218 remaining.frac=(kc.remaining).frac-ini_amount_with_fee.frac 219 + CASE 220 WHEN (kc.remaining).frac < ini_amount_with_fee.frac 221 THEN 100000000 222 ELSE 0 223 END, 224 remaining.val=(kc.remaining).val-ini_amount_with_fee.val 225 - CASE 226 WHEN (kc.remaining).frac < ini_amount_with_fee.frac 227 THEN 1 228 ELSE 0 229 END 230 WHERE coin_pub=ini_coin_pub 231 AND ( ((kc.remaining).val > ini_amount_with_fee.val) OR 232 ( ((kc.remaining).frac >= ini_amount_with_fee.frac) AND 233 ((kc.remaining).val >= ini_amount_with_fee.val) ) ); 234 235 IF NOT FOUND 236 THEN 237 -- Insufficient balance. 238 -- Note: C arrays are 0 indexed, but i started at 1 239 out_insufficient_balance_coin_index=i-1; 240 RETURN; 241 END IF; 242 243 IF my_update 244 THEN 245 -- Prepare to update totals in batch_deposits 246 SELECT * 247 INTO my_total 248 FROM amount_add (my_total, 249 ini_amount_with_fee); 250 SELECT * 251 INTO my_total_without_fee 252 FROM amount_add (my_total_without_fee, 253 ini_amount_with_fee); 254 SELECT * 255 INTO my_diff 256 FROM amount_left_minus_right (my_total_without_fee, 257 ini_deposit_fee); 258 my_total_without_fee = my_diff.diff; 259 END IF; 260 END IF; 261 END LOOP; -- end FOR all coins 262 263 IF my_update 264 THEN 265 UPDATE batch_deposits 266 SET total_amount = my_total 267 ,total_without_fee = my_total_without_fee 268 WHERE batch_deposit_serial_id = bdsi 269 AND merchant_pub = in_merchant_pub 270 AND h_contract_terms = in_h_contract_terms; 271 out_accumulated_total_without_fee = my_total_without_fee; 272 END IF; 273 274 END $$;