diff --git a/src/_zkapauthorizer/model.py b/src/_zkapauthorizer/model.py
index 7a29f671bb0262c1552ddf3b80d33c26782603b6..59fb4ee9e48cfe8a572bab41ac018f75fdf8e409 100644
--- a/src/_zkapauthorizer/model.py
+++ b/src/_zkapauthorizer/model.py
@@ -433,6 +433,14 @@ class VoucherStore(object):
                 "Cannot insert tokens for unknown voucher; add voucher first"
             )
 
+        cursor.execute(
+            """
+            SELECT [counter] FROM [vouchers] WHERE [number] = ?
+            """,
+            (voucher.decode("ascii"),),
+        )
+        (new_counter,) = cursor.fetchone()
+
         cursor.executemany(
             """
             INSERT INTO [unblinded-tokens] ([token], [redemption-group]) VALUES (?, ?)
@@ -442,63 +450,18 @@ class VoucherStore(object):
                 for token in unblinded_tokens
             ),
         )
-        self._delete_corresponding_tokens(cursor, voucher, unblinded_tokens)
+        self._delete_corresponding_tokens(cursor, voucher, new_counter - 1)
 
-    def _delete_corresponding_tokens(self, cursor, voucher : bytes, unblinded_tokens : List["UnblindedToken"]) -> None:
+    def _delete_corresponding_tokens(self, cursor, voucher : bytes, counter : int) -> None:
         """
-        Delete rows from the [tokens] table corresponding to the given unblinded
-        tokens.
+        Delete rows from the [tokens] table corresponding to the given redemption
+        group.
         """
-        # The only way to match tokens with unblinded tokens is to compare the
-        # preimages they each contain.  Unfortunately this means we have to
-        # load all of the tokens from the database.  Hopefully this will never
-        # be a truly huge number because we clean up the table as we make
-        # progress on redemption.
-        def token_preimage(token_b64 : str) -> bytes:
-            # challenge-bypass-ristretto-ffi does not expose a preimage
-            # accessor for tokens. :( We will try to get it to do so.
-            # Meanwhile...
-            token_bytes = b64decode(token_b64)
-            preimage_bytes = token_bytes[:64]
-            return preimage_bytes
-
-        def unblinded_token_preimage(unblinded_token : UnblindedToken) -> bytes:
-            # UnblindedToken exposes a preimage accessor but we have the wrong
-            # kind...
-            unblinded_token_obj = _UnderlyingUnblindedToken.decode_base64(unblinded_token.unblinded_token)
-            preimage_obj = unblinded_token_obj.preimage()
-            preimage_b64 = preimage_obj.encode_base64()
-            preimage_bytes = b64decode(preimage_b64)
-            return preimage_bytes
-
-        # Get the preimages for the unblinded tokens in an easily-querable
-        # structure.
-        preimages = set(map(unblinded_token_preimage, unblinded_tokens))
-        # Load tokens from the database for the comparison.  We can also limit
-        # this search to tokens related to the specific voucher that we used
-        # for redemption.
         cursor.execute(
-            "SELECT [text] FROM [tokens] WHERE [voucher] = ?",
-            (voucher.decode("ascii"),),
-        )
-        tokens_to_delete = []
-        for rows in iter(cursor.fetchmany, []):
-            for (token,) in rows:
-                preimage = token_preimage(token)
-                if preimage in preimages:
-                    # This token has a preimage that matches the preimage of
-                    # one of the unblinded tokens.  This means this is a token
-                    # which was signed.  This means we can drop this token
-                    # now.  Create the tuple now since we'll need it to
-                    # execute the SQL below.
-                    tokens_to_delete.append((token,))
-
-        # Now delete them.
-        cursor.executemany(
             """
-            DELETE FROM [tokens] WHERE [text] = ?
+            DELETE FROM [tokens] WHERE [voucher] = ? AND [counter] = ?
             """,
-            tokens_to_delete,
+            (voucher.decode("ascii"), counter),
         )
 
     @with_cursor