1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
--
-- This file is part of TALER
-- Copyright (C) 2014--2023 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 <http://www.gnu.org/licenses/>
--
CREATE OR REPLACE FUNCTION exchange_do_deposit(
-- For batch_deposits
IN in_shard INT8,
IN in_merchant_pub BYTEA,
IN in_wallet_timestamp INT8,
IN in_exchange_timestamp INT8,
IN in_refund_deadline INT8,
IN in_wire_deadline INT8,
IN in_h_contract_terms BYTEA,
IN in_wallet_data_hash BYTEA, -- can be NULL
IN in_wire_salt BYTEA,
IN in_wire_target_h_payto BYTEA,
IN in_policy_details_serial_id INT8, -- can be NULL
IN in_policy_blocked BOOLEAN,
-- For wire_targets
IN in_receiver_wire_account TEXT,
-- For coin_deposits
IN ina_coin_pub BYTEA[],
IN ina_coin_sig BYTEA[],
IN ina_amount_with_fee taler_amount[],
OUT out_exchange_timestamp INT8,
OUT out_insufficient_balance_coin_index INT4, -- index of coin with bad balance, NULL if none
OUT out_conflict BOOL
)
LANGUAGE plpgsql
AS $$
DECLARE
wtsi INT8; -- wire target serial id
bdsi INT8; -- batch_deposits serial id
curs REFCURSOR;
i INT4;
ini_amount_with_fee taler_amount;
ini_coin_pub BYTEA;
ini_coin_sig BYTEA;
BEGIN
-- Shards:
-- INSERT wire_targets (by h_payto), ON CONFLICT DO NOTHING;
-- INSERT batch_deposits (by shard, merchant_pub), ON CONFLICT idempotency check;
-- INSERT[] coin_deposits (by coin_pub), ON CONFLICT idempotency check;
-- UPDATE[] known_coins (by coin_pub)
-- First, get or create the 'wtsi'
INSERT INTO wire_targets
(wire_target_h_payto
,payto_uri)
VALUES
(in_wire_target_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 wire_targets
WHERE
wire_target_h_payto=in_wire_target_h_payto;
END IF;
-- Second, create the batch_deposits entry
INSERT INTO batch_deposits
(shard
,merchant_pub
,wallet_timestamp
,exchange_timestamp
,refund_deadline
,wire_deadline
,h_contract_terms
,wallet_data_hash
,wire_salt
,wire_target_h_payto
,policy_details_serial_id
,policy_blocked
)
VALUES
(in_shard
,in_merchant_pub
,in_wallet_timestamp
,in_exchange_timestamp
,in_refund_deadline
,in_wire_deadline
,in_h_contract_terms
,in_wallet_data_hash
,in_wire_salt
,in_wire_target_h_payto
,in_policy_details_serial_id
,in_policy_blocked)
ON CONFLICT DO NOTHING -- for CONFLICT ON (merchant_pub, h_contract_terms)
RETURNING
batch_deposit_serial_id
INTO
bdsi;
IF NOT FOUND
THEN
-- Idempotency check: see if an identical record exists.
-- We do select over merchant_pub, h_contract_terms and wire_target_h_payto
-- first to maximally increase the chance of using the existing index.
SELECT
exchange_timestamp
,batch_deposit_serial_id
INTO
out_exchange_timestamp
,bdsi
FROM batch_deposits
WHERE shard=in_shard
AND merchant_pub=in_merchant_pub
AND h_contract_terms=in_h_contract_terms
AND wire_target_h_payto=in_wire_target_h_payto
-- now check the rest, too
AND ( (wallet_data_hash=in_wallet_data_hash) OR
(wallet_data_hash IS NULL AND in_wallet_data_hash IS NULL) )
AND wire_salt=in_wire_salt
AND wallet_timestamp=in_wallet_timestamp
AND refund_deadline=in_refund_deadline
AND wire_deadline=in_wire_deadline
AND ( (policy_details_serial_id=in_policy_details_serial_id) OR
(policy_details_serial_id IS NULL AND in_policy_details_serial_id IS NULL) );
IF NOT FOUND
THEN
-- Deposit exists, but with *strange* differences. Not allowed.
-- FIXME #8002: Surely we need to provide the client more data in this case.
out_conflict=TRUE;
RETURN;
END IF;
END IF;
out_conflict=FALSE;
-- Deposit each coin
FOR i IN 1..array_length(ina_coin_pub,1)
LOOP
ini_coin_pub = ina_coin_pub[i];
ini_coin_sig = ina_coin_sig[i];
ini_amount_with_fee = ina_amount_with_fee[i];
INSERT INTO coin_deposits
(batch_deposit_serial_id
,coin_pub
,coin_sig
,amount_with_fee
)
VALUES
(bdsi
,ini_coin_pub
,ini_coin_sig
,ini_amount_with_fee
)
ON CONFLICT DO NOTHING;
IF FOUND
THEN
-- Insert did happen, update balance in known_coins!
UPDATE known_coins kc
SET
remaining.frac=(kc.remaining).frac-ini_amount_with_fee.frac
+ CASE
WHEN (kc.remaining).frac < ini_amount_with_fee.frac
THEN 100000000
ELSE 0
END,
remaining.val=(kc.remaining).val-ini_amount_with_fee.val
- CASE
WHEN (kc.remaining).frac < ini_amount_with_fee.frac
THEN 1
ELSE 0
END
WHERE coin_pub=ini_coin_pub
AND ( ((kc.remaining).val > ini_amount_with_fee.val) OR
( ((kc.remaining).frac >= ini_amount_with_fee.frac) AND
((kc.remaining).val >= ini_amount_with_fee.val) ) );
IF NOT FOUND
THEN
-- Insufficient balance.
-- Note: C arrays are 0 indexed, but i started at 1
out_insufficient_balance_coin_index=i-1;
RETURN;
END IF;
END IF;
END LOOP; -- end FOR all coins
END $$;
|