-- -- This file is part of TALER -- Copyright (C) 2014--2022 Taler Systems SA -- -- TALER is free software; you can redistribute it and/or modify it under the -- terms of the GNU General Public License as published by the Free Software -- Foundation; either version 3, or (at your option) any later version. -- -- TALER is distributed in the hope that it will be useful, but WITHOUT ANY -- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -- A PARTICULAR PURPOSE. See the GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License along with -- TALER; see the file COPYING. If not, see -- CREATE OR REPLACE FUNCTION exchange_do_deposit( IN in_amount_with_fee_val INT8, IN in_amount_with_fee_frac INT4, IN in_h_contract_terms BYTEA, IN in_wire_salt BYTEA, IN in_wallet_timestamp INT8, IN in_exchange_timestamp INT8, IN in_refund_deadline INT8, IN in_wire_deadline INT8, IN in_merchant_pub BYTEA, IN in_receiver_wire_account VARCHAR, IN in_h_payto BYTEA, IN in_known_coin_id INT8, IN in_coin_pub BYTEA, IN in_coin_sig BYTEA, IN in_shard INT8, IN in_policy_blocked BOOLEAN, IN in_policy_details_serial_id INT8, OUT out_exchange_timestamp INT8, OUT out_balance_ok BOOLEAN, OUT out_conflict BOOLEAN) LANGUAGE plpgsql AS $$ DECLARE wtsi INT8; -- wire target serial id BEGIN -- Shards: INSERT policy_details (by policy_details_serial_id) -- INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING; -- INSERT deposits (by coin_pub, shard), ON CONFLICT DO NOTHING; -- UPDATE known_coins (by coin_pub) INSERT INTO exchange.wire_targets (wire_target_h_payto ,payto_uri) VALUES (in_h_payto ,in_receiver_wire_account) ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto) RETURNING wire_target_serial_id INTO wtsi; IF NOT FOUND THEN SELECT wire_target_serial_id INTO wtsi FROM exchange.wire_targets WHERE wire_target_h_payto=in_h_payto; END IF; INSERT INTO exchange.deposits (shard ,coin_pub ,known_coin_id ,amount_with_fee_val ,amount_with_fee_frac ,wallet_timestamp ,exchange_timestamp ,refund_deadline ,wire_deadline ,merchant_pub ,h_contract_terms ,coin_sig ,wire_salt ,wire_target_h_payto ,policy_blocked ,policy_details_serial_id ) VALUES (in_shard ,in_coin_pub ,in_known_coin_id ,in_amount_with_fee_val ,in_amount_with_fee_frac ,in_wallet_timestamp ,in_exchange_timestamp ,in_refund_deadline ,in_wire_deadline ,in_merchant_pub ,in_h_contract_terms ,in_coin_sig ,in_wire_salt ,in_h_payto ,in_policy_blocked ,in_policy_details_serial_id) ON CONFLICT DO NOTHING; IF NOT FOUND THEN -- Idempotency check: see if an identical record exists. -- Note that by checking 'coin_sig', we implicitly check -- identity over everything that the signature covers. -- We do select over merchant_pub and wire_target_h_payto -- primarily here to maximally use the existing index. SELECT exchange_timestamp INTO out_exchange_timestamp FROM exchange.deposits WHERE shard=in_shard AND merchant_pub=in_merchant_pub AND wire_target_h_payto=in_h_payto AND coin_pub=in_coin_pub AND coin_sig=in_coin_sig; -- AND policy_details_serial_id=in_policy_details_serial_id; -- FIXME: is this required for idempotency? IF NOT FOUND THEN -- Deposit exists, but with differences. Not allowed. out_balance_ok=FALSE; out_conflict=TRUE; RETURN; END IF; -- Idempotent request known, return success. out_balance_ok=TRUE; out_conflict=FALSE; RETURN; END IF; out_exchange_timestamp=in_exchange_timestamp; -- Check and update balance of the coin. UPDATE known_coins SET remaining_frac=remaining_frac-in_amount_with_fee_frac + CASE WHEN remaining_frac < in_amount_with_fee_frac THEN 100000000 ELSE 0 END, remaining_val=remaining_val-in_amount_with_fee_val - CASE WHEN remaining_frac < in_amount_with_fee_frac THEN 1 ELSE 0 END WHERE coin_pub=in_coin_pub AND ( (remaining_val > in_amount_with_fee_val) OR ( (remaining_frac >= in_amount_with_fee_frac) AND (remaining_val >= in_amount_with_fee_val) ) ); IF NOT FOUND THEN -- Insufficient balance. out_balance_ok=FALSE; out_conflict=FALSE; RETURN; END IF; -- Everything fine, return success! out_balance_ok=TRUE; out_conflict=FALSE; END $$;