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
|
CREATE OR REPLACE FUNCTION exchange_do_withdraw(
IN amount_val INT8,
IN amount_frac INT4,
IN h_denom_pub BYTEA,
IN rpub BYTEA,
IN reserve_sig BYTEA,
IN h_coin_envelope BYTEA,
IN denom_sig BYTEA,
IN now INT8,
IN min_reserve_gc INT8,
OUT reserve_found BOOLEAN,
OUT balance_ok BOOLEAN,
OUT kycok BOOLEAN,
OUT ruuid INT8,
OUT account_uuid INT8)
LANGUAGE plpgsql
AS $$
DECLARE
reserve_gc INT8;
DECLARE
denom_serial INT8;
DECLARE
reserve_val INT8;
DECLARE
reserve_frac INT4;
BEGIN
SELECT denominations_serial INTO denom_serial
FROM denominations
WHERE denom_pub_hash=h_denom_pub;
IF NOT FOUND
THEN
-- denomination unknown, should be impossible!
reserve_found=FALSE;
balance_ok=FALSE;
kycok=FALSE;
ruuid=0;
account_uuid=0;
ASSERT false, 'denomination unknown';
RETURN;
END IF;
UPDATE reserves SET
gc_date=GREATEST(gc_date, min_reserve_gc)
,current_balance_val=current_balance_val - amount_val
- CASE WHEN (current_balance_frac < amount_frac)
THEN 1
ELSE 0
END
,current_balance_frac=current_balance_frac - amount_frac
+ CASE WHEN (current_balance_frac < amount_frac)
THEN 100000000
ELSE 0
END
WHERE reserves.reserve_pub=rpub
AND ( (current_balance_val > amount_val) OR
( (current_balance_val = amount_val) AND
(current_balance_frac >= amount_frac) ) );
balance_ok=FOUND;
-- Obtain KYC status based on the last wire transfer into
-- this reserve. FIXME: likely not adequate for reserves that got P2P transfers!
SELECT
kyc_ok
,wire_source_serial_id
,reserve_uuid
INTO
kycok
,account_uuid
,ruuid
FROM reserves
JOIN reserves_in USING (reserve_uuid)
JOIN wire_targets ON (wire_source_serial_id = wire_target_serial_id)
WHERE reserves.reserve_pub=rpub
LIMIT 1; -- limit 1 should not be required (without p2p transfers)
IF NOT FOUND
THEN
-- reserve unknown
reserve_found=FALSE;
balance_ok=FALSE;
kycok=FALSE;
account_uuid=0;
RETURN;
END IF;
reserve_found=TRUE;
-- We optimistically insert, and then on conflict declare
-- the query successful due to idempotency.
INSERT INTO reserves_out
(h_blind_ev
,denominations_serial
,denom_sig
,reserve_uuid
,reserve_sig
,execution_date
,amount_with_fee_val
,amount_with_fee_frac)
VALUES
(h_coin_envelope
,denom_serial
,denom_sig
,ruuid
,reserve_sig
,now
,amount_val
,amount_frac)
ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
-- idempotent query, all constraints must be satisfied
balance_ok=TRUE;
-- rollback any potential balance update we may have made
ROLLBACK;
START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
RETURN;
END IF;
END $$;
COMMENT ON FUNCTION exchange_do_withdraw(INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result';
|