aboutsummaryrefslogtreecommitdiff
path: root/src/exchangedb/melt.sql
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchangedb/melt.sql')
-rw-r--r--src/exchangedb/melt.sql280
1 files changed, 280 insertions, 0 deletions
diff --git a/src/exchangedb/melt.sql b/src/exchangedb/melt.sql
new file mode 100644
index 000000000..af1aa8d4a
--- /dev/null
+++ b/src/exchangedb/melt.sql
@@ -0,0 +1,280 @@
+
+-- Everything in one big transaction
+-- BEGIN;
+
+-- Check patch versioning is in place.
+-- SELECT _v.register_patch('exchange-000x', NULL, NULL);
+
+CREATE OR REPLACE FUNCTION exchange_do_melt(
+ IN denom_val INT8, -- value of the denomination of the coin
+ IN denom_frac INT4, -- value of the denomination of the coin
+ IN amount_val INT8, -- requested melt amount (with fee)
+ IN amount_frac INT4, -- requested melt amount (with fee)
+ IN in_rc BYTEA, -- refresh session hash
+ IN in_coin_pub BYTEA, -- coin public key
+ IN coin_sig BYTEA, -- melt signature
+ IN in_noreveal_index INT4, -- suggested random noreveal index
+ IN zombie_required BOOLEAN, -- do we need a zombie coin?
+ OUT out_noreval_index INT4, -- noreveal index to actually use
+ OUT balance_ok BOOLEAN, -- balance satisfied?
+ OUT zombie_ok BOOLEAN, -- zombie satisfied?
+ OUT melt_ok BOOLEAN) -- everything OK?
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ coin_uuid INT8; -- known_coin_id of coin_pub
+DECLARE
+ tmp_val INT8; -- temporary result
+DECLARE
+ tmp_frac INT8; -- temporary result
+DECLARE
+ spent_val INT8; -- how much of coin was spent?
+DECLARE
+ spent_frac INT8; -- how much of coin was spent?
+DECLARE
+ unspent_val INT8; -- how much of coin was refunded?
+DECLARE
+ unspent_frac INT8; -- how much of coin was refunded?
+BEGIN
+
+SELECT known_coin_id INTO coin_uuid
+ FROM known_coins
+ WHERE coin_pub=in_coin_pub;
+
+IF NOT FOUND
+THEN
+ -- coin unknown, should be impossible!
+ out_noreveal_index=-1;
+ balance_ok=FALSE;
+ zombie_ok=FALSE;
+ melt_ok=FALSE;
+ ASSERT false, 'coin unknown';
+ RETURN;
+END IF;
+
+-- We optimistically insert, and then on conflict declare
+-- the query successful due to idempotency.
+INSERT INTO refresh_commitments
+ (rc
+ ,old_known_coin_id
+ ,old_coin_sig
+ ,amount_with_fee_val
+ ,amount_with_fee_frac
+ ,noreveal_index)
+VALUES
+ (in_rc
+ ,coin_uuid
+ ,coin_sig
+ ,amount_val
+ ,amount_frac
+ ,in_noreveal_index)
+ON CONFLICT DO NOTHING;
+
+IF FOUND
+THEN
+ -- already melted, get noreveal_index
+ SELECT noreveal_index INTO out_noreveal_index
+ FROM refresh_commitments
+ WHERE rc=in_rc ;
+ balance_ok=TRUE;
+ zombie_ok=TRUE;
+ melt_ok=TRUE;
+ RETURN;
+END IF;
+
+-- Need to check for sufficient balance...
+spent_val = 0;
+spent_frac = 0;
+unspent_val = 0;
+unspent_frac = 0;
+
+SELECT
+ SUM(amount_with_fee_val) -- overflow here is not plausible
+ ,SUM(CAST(amount_with_fee_frac AS INT8)) -- compute using 64 bits
+ INTO
+ tmp_val
+ ,tmp_frac
+ FROM deposits
+ WHERE known_coin_id=coin_uuid;
+
+spent_val = spent_val + tmp_val;
+spent_frac = spent_frac + tmp_frac;
+
+SELECT
+ SUM(amount_with_fee_val) -- overflow here is not plausible
+ ,SUM(CAST(amount_with_fee_frac AS INT8)) -- compute using 64 bits
+ INTO
+ tmp_val
+ ,tmp_frac
+ FROM refresh_commitments
+ WHERE old_known_coin_id=coin_uuid;
+
+spent_val = spent_val + tmp_val;
+spent_frac = spent_frac + tmp_frac;
+
+SELECT
+ SUM(amount_with_fee_val) -- overflow here is not plausible
+ ,SUM(CAST(amount_with_fee_frac AS INT8)) -- compute using 64 bits
+ INTO
+ tmp_val
+ ,tmp_frac
+ FROM refunds
+ WHERE known_coin_id=coin_uuid;
+
+unspent_val = unspent_val + tmp_val;
+unspent_frac = unspent_frac + tmp_frac;
+
+SELECT
+ SUM(amount_val) -- overflow here is not plausible
+ ,SUM(CAST(amount_frac AS INT8)) -- compute using 64 bits
+ INTO
+ tmp_val
+ ,tmp_frac
+ FROM recoup_refresh
+ WHERE known_coin_id=coin_uuid;
+
+unspent_val = unspent_val + tmp_val;
+unspent_frac = unspent_frac + tmp_frac;
+
+SELECT
+ SUM(amount_val) -- overflow here is not plausible
+ ,SUM(CAST(amount_frac AS INT8)) -- compute using 64 bits
+ INTO
+ tmp_val
+ ,tmp_frac
+ FROM recoup
+ WHERE known_coin_id=coin_uuid;
+
+spent_val = spent_val + tmp_val;
+spent_frac = spent_frac + tmp_frac;
+
+SELECT
+ SUM(amount_val) -- overflow here is not plausible
+ ,SUM(CAST(amount_frac AS INT8)) -- compute using 64 bits
+ INTO
+ tmp_val
+ ,tmp_frac
+ FROM recoup_refresh
+ JOIN refresh_revealed_coins rrc
+ USING (rrc_serial)
+ JOIN refresh_commitments rfc
+ ON (rrc.melt_serial_id = rfc.melt_serial_id)
+ WHERE rfc.old_known_coin_id=coin_uuid;
+
+spent_val = spent_val + tmp_val;
+spent_frac = spent_frac + tmp_frac;
+
+
+------------------- TBD from here
+
+SELECT
+ reserve_uuid
+ ,current_balance_val
+ ,current_balance_frac_uuid
+ ,expiration_date
+ ,gc_date
+ INTO
+ reserve_uuid
+ ,reserve_val
+ ,reserve_frac
+ ,reserve_gc
+ FROM reserves
+ WHERE reserve_pub=reserve_pub;
+
+IF NOT FOUND
+THEN
+ -- reserve unknown
+ reserve_found=FALSE;
+ balance_ok=FALSE;
+ kyc_ok=FALSE;
+ RETURN;
+END IF;
+
+-- We optimistically insert, and then on conflict declare
+-- the query successful due to idempotency.
+INSERT INTO reserves_out
+ (h_blind_ev
+ ,denom_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
+ ,reserve_uuid
+ ,reserve_sig
+ ,now
+ ,amount_val
+ ,amount_frac)
+ON CONFLICT DO NOTHING;
+
+IF NOT FOUND
+THEN
+ -- idempotent query, all constraints must be satisfied
+ reserve_found=TRUE;
+ balance_ok=TRUE;
+ kyc_ok=TRUE;
+ RETURN;
+END IF;
+
+-- Check reserve balance is sufficient.
+IF (reserve_val > amount_val)
+THEN
+ IF (reserve_frac > amount_frac)
+ THEN
+ reserve_val=reserve_val - amount_val;
+ reserve_frac=reserve_frac - amount_frac;
+ ELSE
+ reserve_val=reserve_val - amount_val - 1;
+ reserve_frac=reserve_frac + 100000000 - amount_frac;
+ END IF;
+ELSE
+ IF (reserve_val == amount_val) AND (reserve_frac >= amount_frac)
+ THEN
+ reserve_val=0;
+ reserve_frac=reserve_frac - amount_frac;
+ ELSE
+ reserve_found=TRUE;
+ balance_ok=FALSE;
+ kyc_ok=FALSE; -- we do not really know or care
+ RETURN;
+ END IF;
+END IF;
+
+-- Calculate new expiration dates.
+min_reserve_gc=MAX(min_reserve_gc,reserve_gc);
+
+-- Update reserve balance.
+UPDATE reserves SET
+ gc_date=min_reserve_gc
+ ,current_balance_val=reserve_val
+ ,current_balance_frac=reserve_frac
+WHERE
+ reserve_uuid=reserve_uuid;
+
+reserve_found=TRUE;
+balance_ok=TRUE;
+
+-- 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
+ INTO kyc_ok
+ FROM reserves_in
+ JOIN wire_targets USING (wire_target_serial_id)
+ WHERE reserve_uuid=reserve_uuid
+ LIMIT 1; -- limit 1 should not be required (without p2p transfers)
+
+
+
+END $$;
+
+COMMENT ON FUNCTION exchange_do_melt(INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
+ IS 'Checks whether the coin has sufficient balance for a melt operation (or the request is repeated and was previously approved) and if so updates the database with the result';
+
+
+-- Complete transaction
+-- COMMIT;