depolymerizer-bitcoin-procedures.sql (5783B)
1 -- 2 -- This file is part of TALER 3 -- Copyright (C) 2025 Taler Systems SA 4 -- 5 -- TALER is free software; you can redistribute it and/or modify it under the 6 -- terms of the GNU General Public License as published by the Free Software 7 -- Foundation; either version 3, or (at your option) any later version. 8 -- 9 -- TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 -- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 -- A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 -- 13 -- You should have received a copy of the GNU General Public License along with 14 -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 16 SET search_path TO depolymerizer_bitcoin; 17 18 -- Remove all existing functions 19 DO 20 $do$ 21 DECLARE 22 _sql text; 23 BEGIN 24 SELECT INTO _sql 25 string_agg(format('DROP %s %s CASCADE;' 26 , CASE prokind 27 WHEN 'f' THEN 'FUNCTION' 28 WHEN 'p' THEN 'PROCEDURE' 29 END 30 , oid::regprocedure) 31 , E'\n') 32 FROM pg_proc 33 WHERE pronamespace = 'depolymerizer_bitcoin'::regnamespace; 34 35 IF _sql IS NOT NULL THEN 36 EXECUTE _sql; 37 END IF; 38 END 39 $do$; 40 41 CREATE FUNCTION taler_transfer( 42 IN in_amount taler_amount, 43 IN in_exchange_base_url TEXT, 44 IN in_credit_acc TEXT, 45 IN in_credit_name TEXT, 46 IN in_request_uid BYTEA, 47 IN in_wtid BYTEA, 48 IN in_now INT8, 49 -- Error status 50 OUT out_request_uid_reuse BOOLEAN, 51 OUT out_wtid_reuse BOOLEAN, 52 -- Success return 53 OUT out_transfer_row_id INT8, 54 OUT out_created_at INT8 55 ) 56 LANGUAGE plpgsql AS $$ 57 BEGIN 58 -- Check for idempotence and conflict 59 SELECT (amount != in_amount 60 OR credit_acc != in_credit_acc 61 OR credit_name != in_credit_name 62 OR exchange_url != in_exchange_base_url 63 OR wtid != in_wtid) 64 ,id, created 65 INTO out_request_uid_reuse, out_transfer_row_id, out_created_at 66 FROM tx_out 67 WHERE request_uid = in_request_uid; 68 IF FOUND THEN 69 out_wtid_reuse=FALSE; 70 RETURN; 71 END IF; 72 out_request_uid_reuse=FALSE; 73 74 -- Register exchange 75 INSERT INTO tx_out ( 76 amount, 77 exchange_url, 78 credit_acc, 79 credit_name, 80 request_uid, 81 wtid, 82 created, 83 status 84 ) VALUES ( 85 in_amount, 86 in_exchange_base_url, 87 in_credit_acc, 88 in_credit_name, 89 in_request_uid, 90 in_wtid, 91 in_now, 92 'requested' 93 ) ON CONFLICT (wtid) DO NOTHING 94 RETURNING id, created INTO out_transfer_row_id, out_created_at; 95 out_wtid_reuse=NOT FOUND; 96 IF out_wtid_reuse THEN 97 RETURN; 98 END IF; 99 -- Notify new transaction 100 PERFORM pg_notify('taler_out', out_transfer_row_id || ''); 101 END $$; 102 COMMENT ON FUNCTION taler_transfer IS 'Create an outgoing taler transaction and register it'; 103 104 CREATE FUNCTION register_tx_in( 105 IN in_amount taler_amount, 106 IN in_debit_acc TEXT, 107 IN in_reserve_pub BYTEA, 108 IN in_received INT8, 109 IN in_txid BYTEA, 110 -- Error status 111 OUT out_reserve_pub_reuse BOOLEAN, 112 -- Success return 113 OUT out_tx_row_id INT8, 114 OUT out_valued_at INT8, 115 OUT out_new BOOLEAN 116 ) 117 LANGUAGE plpgsql AS $$ 118 BEGIN 119 -- Check for idempotence and conflict 120 SELECT (amount != in_amount OR debit_acc != in_debit_acc OR txid != in_txid), id, received 121 INTO out_reserve_pub_reuse, out_tx_row_id, out_valued_at 122 FROM tx_in 123 WHERE reserve_pub = in_reserve_pub; 124 out_new=NOT FOUND; 125 IF FOUND THEN 126 RETURN; 127 END IF; 128 out_reserve_pub_reuse=false; 129 130 -- Insert new incoming transaction 131 INSERT INTO tx_in ( 132 txid, 133 amount, 134 debit_acc, 135 reserve_pub, 136 received 137 ) VALUES ( 138 in_txid, 139 in_amount, 140 in_debit_acc, 141 in_reserve_pub, 142 in_received 143 ) RETURNING id, received INTO out_tx_row_id, out_valued_at; 144 PERFORM pg_notify('taler_in', out_tx_row_id || ''); 145 END $$; 146 COMMENT ON FUNCTION register_tx_in IS 'Register an incoming transaction idempotently'; 147 148 CREATE FUNCTION sync_out( 149 IN in_txid BYTEA, 150 IN in_replace_txid BYTEA, 151 IN in_amount taler_amount, 152 IN in_exchange_base_url TEXT, 153 IN in_credit_acc TEXT, 154 IN in_wtid BYTEA, 155 IN in_created INT8, 156 -- Success return 157 OUT out_new BOOLEAN, 158 OUT out_replaced BOOLEAN, 159 OUT out_recovered BOOLEAN 160 ) 161 LANGUAGE plpgsql AS $$ 162 DECLARE 163 local_id INT8; 164 BEGIN 165 -- Check replacement 166 IF in_replace_txid IS NOT NULL THEN 167 UPDATE tx_out SET txid=in_txid, status='status' WHERE txid=in_replace_txid; 168 IF FOUND THEN 169 out_new=FALSE; 170 out_replaced=FALSE; 171 out_recovered=FALSE; 172 RETURN; 173 END IF; 174 END IF; 175 out_replaced=FALSE; 176 out_new=NOT EXISTS(SELECT FROM tx_out WHERE wtid = in_wtid); 177 178 IF out_new THEN 179 INSERT INTO tx_out ( 180 amount, 181 wtid, 182 credit_acc, 183 exchange_url, 184 status, 185 txid, 186 created, 187 request_uid 188 ) VALUES ( 189 in_amount, 190 in_wtid, 191 in_credit_acc, 192 in_exchange_base_url, 193 'sent', 194 in_txid, 195 in_created, 196 NULL 197 ) RETURNING id INTO local_id; 198 out_recovered=FALSE; 199 PERFORM pg_notify('taler_out', local_id || ''); 200 ELSE 201 UPDATE tx_out SET txid=in_txid, status='sent' WHERE status='requested' AND wtid=in_wtid; 202 out_recovered=FOUND; 203 END IF; 204 END $$; 205 COMMENT ON FUNCTION sync_out IS 'Sync a debit blockchain state with local state'; 206 207 CREATE FUNCTION sync_bounce( 208 IN in_txid BYTEA, 209 IN in_bounced BYTEA, 210 IN in_created INT8, 211 -- Success return 212 OUT out_new BOOLEAN, 213 OUT out_recovered BOOLEAN 214 ) 215 LANGUAGE plpgsql AS $$ 216 BEGIN 217 out_new=NOT EXISTS(SELECT FROM bounce WHERE bounced = in_bounced); 218 219 IF out_new THEN 220 INSERT INTO bounce ( 221 created, 222 bounced, 223 txid, 224 status 225 ) VALUES ( 226 in_created, 227 in_bounced, 228 in_txid, 229 'sent' 230 ); 231 out_recovered=false; 232 ELSE 233 UPDATE bounce SET txid=in_txid, status='sent' WHERE status!='sent' AND bounced=in_bounced; 234 out_recovered=FOUND; 235 END IF; 236 END $$; 237 COMMENT ON FUNCTION sync_bounce IS 'Sync a bounce blockchain state with local state';