sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

syncdb_delete_block_TR.sql (7170B)


      1 --
      2 -- This file is part of TALER
      3 -- Copyright (C) 2026 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 
     17 -- The deletion relinks both neighbours, so the client uploads their new
     18 -- signatures (in_prev_upload_sig, in_next_upload_sig).  Those signatures
     19 -- cover the neighbours as the caller read them outside this
     20 -- transaction, so the whole signed context has to be re-checked here or
     21 -- a concurrent write could leave a neighbour with a signature over links
     22 -- it no longer has: the transaction re-checks them against in_prev_hash
     23 -- and in_prev_prev_nonce, resp. in_next_hash and in_next_next_nonce.
     24 -- The signed refs_hash needs no separate check, since
     25 -- sync_do_update_block() is the only thing that changes it and it always
     26 -- changes the block hash along with it.
     27 DROP FUNCTION IF EXISTS sync_do_delete_block;
     28 CREATE FUNCTION sync_do_delete_block (
     29   IN in_account_pub BYTEA,
     30   IN in_block_nonce BYTEA,
     31   IN in_prev_nonce BYTEA,
     32   IN in_next_nonce BYTEA,
     33   IN in_block_hash BYTEA,
     34   IN ina_object_uids BYTEA[],
     35   IN ina_object_incs INT2[],
     36   IN in_prev_upload_sig BYTEA,
     37   IN in_prev_hash BYTEA,
     38   IN in_prev_prev_nonce BYTEA,
     39   IN in_next_upload_sig BYTEA,
     40   IN in_next_hash BYTEA,
     41   IN in_next_next_nonce BYTEA,
     42   OUT out_account_missing BOOLEAN,
     43   OUT out_block_missing BOOLEAN,
     44   OUT out_outdated_write BOOLEAN,
     45   OUT out_refs_missing BOOLEAN,
     46    OUT out_negative_refcount BOOLEAN,
     47    OUT out_deleted BOOLEAN,
     48    OUT out_refs_updated BOOLEAN)
     49 LANGUAGE plpgsql
     50 AS $$
     51 DECLARE
     52   my_prev_nonce BYTEA;
     53   my_next_nonce BYTEA;
     54   my_block_hash BYTEA;
     55   my_n_hash BYTEA;
     56   my_n_link BYTEA;
     57 BEGIN
     58   out_account_missing := FALSE;
     59   out_block_missing := FALSE;
     60   out_outdated_write := FALSE;
     61   out_refs_missing := FALSE;
     62   out_negative_refcount := FALSE;
     63   out_deleted := FALSE;
     64   out_refs_updated := FALSE;
     65 
     66   -- Check if account exists and has valid payment
     67   SELECT a.out_account_missing
     68     INTO out_account_missing
     69     FROM sync_check_account_payment(in_account_pub) AS a;
     70   IF out_account_missing
     71   THEN
     72     RETURN;
     73   END IF;
     74 
     75   SELECT prev_nonce
     76         ,next_nonce
     77         ,block_hash
     78     INTO my_prev_nonce
     79         ,my_next_nonce
     80         ,my_block_hash
     81     FROM blocks
     82    WHERE account_pub = in_account_pub
     83      AND nonce = in_block_nonce;
     84 
     85   IF NOT FOUND
     86   THEN
     87     out_block_missing := TRUE;
     88     RETURN;
     89   END IF;
     90 
     91   -- Does existing hash matches expected old hash?
     92   -- Does previous and next nonces match expectations?
     93   IF my_block_hash IS DISTINCT FROM in_block_hash OR
     94      my_prev_nonce IS DISTINCT FROM in_prev_nonce OR
     95      my_next_nonce IS DISTINCT FROM in_next_nonce
     96   THEN
     97     out_outdated_write := TRUE;
     98     RETURN;
     99   END IF;
    100 
    101   -- The neighbours must still look the way the relink signatures were
    102   -- verified against (see header comment).  Only the link this
    103   -- transaction does not rewrite is checked; the one it does rewrite is
    104   -- the block being deleted, which was checked above.
    105   IF in_prev_nonce IS NOT NULL
    106   THEN
    107     SELECT block_hash
    108           ,prev_nonce
    109       INTO my_n_hash
    110           ,my_n_link
    111       FROM blocks
    112      WHERE account_pub = in_account_pub
    113        AND nonce = in_prev_nonce;
    114 
    115     IF NOT FOUND
    116        OR my_n_hash IS DISTINCT FROM in_prev_hash
    117        OR my_n_link IS DISTINCT FROM in_prev_prev_nonce
    118     THEN
    119       out_outdated_write := TRUE;
    120       RETURN;
    121     END IF;
    122   END IF;
    123 
    124   IF in_next_nonce IS NOT NULL
    125   THEN
    126     SELECT block_hash
    127           ,next_nonce
    128       INTO my_n_hash
    129           ,my_n_link
    130       FROM blocks
    131      WHERE account_pub = in_account_pub
    132        AND nonce = in_next_nonce;
    133 
    134     IF NOT FOUND
    135        OR my_n_hash IS DISTINCT FROM in_next_hash
    136        OR my_n_link IS DISTINCT FROM in_next_next_nonce
    137     THEN
    138       out_outdated_write := TRUE;
    139       RETURN;
    140     END IF;
    141   END IF;
    142 
    143   -- Check referenced objects exist and no increment underflows,
    144   -- before we touch anything
    145   SELECT c.out_negative_refcount
    146         ,c.out_refs_missing
    147     INTO out_negative_refcount
    148         ,out_refs_missing
    149     FROM sync_check_object_refcounts_pre(in_account_pub,
    150                                          ina_object_uids,
    151                                          ina_object_incs) AS c;
    152   IF out_negative_refcount OR out_refs_missing
    153   THEN
    154     RETURN;
    155   END IF;
    156 
    157   -- Update previous block's next reference (if necessary); the
    158   -- neighbours come from the block we are deleting, so they must exist.
    159   -- Store the relink signature with the neighbour; a relink never
    160   -- changes the data, so old_hash is set to the current block hash.
    161   IF my_prev_nonce IS NOT NULL
    162   THEN
    163     UPDATE blocks
    164        SET next_nonce = my_next_nonce
    165           ,upload_sig = in_prev_upload_sig
    166           ,old_hash = block_hash
    167      WHERE account_pub = in_account_pub
    168        AND nonce = my_prev_nonce;
    169 
    170     IF NOT FOUND
    171     THEN
    172       RAISE EXCEPTION 'previous block % vanished for account %',
    173         my_prev_nonce, in_account_pub;
    174     END IF;
    175   END IF;
    176 
    177   -- Update next block's previous reference (if necessary)
    178   IF my_next_nonce IS NOT NULL
    179   THEN
    180     UPDATE blocks
    181        SET prev_nonce = my_prev_nonce
    182           ,upload_sig = in_next_upload_sig
    183           ,old_hash = block_hash
    184      WHERE account_pub = in_account_pub
    185        AND nonce = my_next_nonce;
    186 
    187     IF NOT FOUND
    188     THEN
    189       RAISE EXCEPTION 'next block % vanished for account %',
    190         my_next_nonce, in_account_pub;
    191     END IF;
    192   END IF;
    193 
    194   -- Update first and last block references (if necessary)
    195   UPDATE accounts
    196      SET first_block = CASE
    197            WHEN my_prev_nonce IS NULL
    198              THEN my_next_nonce
    199            ELSE first_block
    200          END
    201         ,last_block = CASE
    202            WHEN my_next_nonce IS NULL
    203              THEN my_prev_nonce
    204            ELSE last_block
    205          END
    206    WHERE account_pub = in_account_pub;
    207 
    208   -- Delete block
    209   DELETE FROM blocks
    210    WHERE account_pub = in_account_pub
    211      AND nonce = in_block_nonce;
    212 
    213   IF NOT FOUND
    214   THEN
    215     RAISE EXCEPTION 'block % vanished for account %',
    216       in_block_nonce, in_account_pub;
    217   END IF;
    218 
    219   -- Update object refcounts and delete unreferenced objects; the
    220   -- pre-check above means this can only fail on a concurrent change
    221   SELECT u.out_negative_refcount
    222         ,u.out_refs_updated
    223     INTO out_negative_refcount
    224         ,out_refs_updated
    225     FROM sync_update_object_refcounts(in_account_pub,
    226                                       ina_object_uids,
    227                                       ina_object_incs) AS u;
    228   IF NOT out_refs_updated
    229   THEN
    230     RAISE EXCEPTION 'object refcounts changed concurrently for account %',
    231       in_account_pub;
    232   END IF;
    233 
    234   out_deleted := TRUE;
    235 END $$;