summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/exchangedb/withdraw.sql130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/exchangedb/withdraw.sql b/src/exchangedb/withdraw.sql
new file mode 100644
index 000000000..0ac9a73f0
--- /dev/null
+++ b/src/exchangedb/withdraw.sql
@@ -0,0 +1,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';
+