From 6541745fcae12880d0a60a2f82bc45d2ecce571a Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:43:55 +0000 Subject: [PATCH] fix header and test --- plans/2026-08-22-xftp-file-storage-time.md | 2 +- src/Simplex/Messaging/Crypto/Entitlement.hs | 3 +- tests/CoreTests/XFTPStoreTests.hs | 32 ++++++++++----------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/plans/2026-08-22-xftp-file-storage-time.md b/plans/2026-08-22-xftp-file-storage-time.md index b7f4759be..ba1e0b0a4 100644 --- a/plans/2026-08-22-xftp-file-storage-time.md +++ b/plans/2026-08-22-xftp-file-storage-time.md @@ -34,7 +34,7 @@ data EntitlementProof = EntitlementProof Functions and constants: - the disclosed-message encoding: the master key is message 0 and stays undisclosed; `expiresAt`, `entitlementName`, and `extraInfo` are messages 1 to 3 and are disclosed -- the BBS header string `"SimpleX entitlement v1"`, the message count, and the disclosed indexes +- the BBS header string `"SimpleX badges v1"` (shared with chat's badges, which sign under it), the message count, and the disclosed indexes - `generateEntitlementProof :: BBSPublicKey -> EntitlementCredential -> BBSPresHeader -> IO (Either String EntitlementProof)` - `verifyEntitlement :: Map Int BBSPublicKey -> BBSPresHeader -> EntitlementProof -> IO (Maybe Bool)` (the caller supplies the presentation header; the server reconstructs it, the proof never includes it) - the issuer public keys constant `Map Int BBSPublicKey` diff --git a/src/Simplex/Messaging/Crypto/Entitlement.hs b/src/Simplex/Messaging/Crypto/Entitlement.hs index acf6f9b35..049954b10 100644 --- a/src/Simplex/Messaging/Crypto/Entitlement.hs +++ b/src/Simplex/Messaging/Crypto/Entitlement.hs @@ -11,6 +11,7 @@ module Simplex.Messaging.Crypto.Entitlement EntitlementCredential (..), EntitlementProof (..), MasterKey (..), + entitlementBBSHeader, entitlementIssuerKeys, signEntitlement, verifyCredential, @@ -79,7 +80,7 @@ instance Encoding EntitlementProof where pure EntitlementProof {issuerKeyIdx, entProof, entitlement} entitlementBBSHeader :: BBSHeader -entitlementBBSHeader = BBSHeader "SimpleX entitlement v1" +entitlementBBSHeader = BBSHeader "SimpleX badges v1" entitlementMessageCount :: Int entitlementMessageCount = 4 diff --git a/tests/CoreTests/XFTPStoreTests.hs b/tests/CoreTests/XFTPStoreTests.hs index 20c0e77fc..87afb8e68 100644 --- a/tests/CoreTests/XFTPStoreTests.hs +++ b/tests/CoreTests/XFTPStoreTests.hs @@ -75,7 +75,7 @@ testAddGetFileSender = withPgStore $ \st -> do g <- C.newRandom (sk, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sk - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () result <- getFile st SFSender testSenderId case result of Right (FileRec {senderId, fileInfo = fi, createdAt}, key) -> do @@ -92,7 +92,7 @@ testAddGetFileRecipient = withPgStore $ \st -> do (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g (rcpKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () addRecipient st testSenderId (FileRecipient testRecipientId rcpKey) `shouldReturn` Right () result <- getFile st SFRecipient testRecipientId case result of @@ -106,8 +106,8 @@ testDuplicateFile = withPgStore $ \st -> do g <- C.newRandom (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Left DUPLICATE_ + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Left DUPLICATE_ testGetNonexistent :: Expectation testGetNonexistent = withPgStore $ \st -> do @@ -119,7 +119,7 @@ testSetFilePath = withPgStore $ \st -> do g <- C.newRandom (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () setFilePath st testSenderId "/tmp/test_file" `shouldReturn` Right () -- Second setFilePath should fail (file_path IS NULL guard) setFilePath st testSenderId "/tmp/other_file" `shouldReturn` Left AUTH @@ -135,7 +135,7 @@ testDuplicateRecipient = withPgStore $ \st -> do (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g (rcpKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () addRecipient st testSenderId (FileRecipient testRecipientId rcpKey) `shouldReturn` Right () addRecipient st testSenderId (FileRecipient testRecipientId rcpKey) `shouldReturn` Left DUPLICATE_ @@ -145,7 +145,7 @@ testDeleteFileCascade = withPgStore $ \st -> do (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g (rcpKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () addRecipient st testSenderId (FileRecipient testRecipientId rcpKey) `shouldReturn` Right () deleteFile st testSenderId `shouldReturn` Right () -- File and recipient should both be gone @@ -157,7 +157,7 @@ testBlockFile = withPgStore $ \st -> do g <- C.newRandom (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () let blockInfo = BlockingInfo {reason = BRContent, notice = Nothing} blockFile st testSenderId blockInfo False `shouldReturn` Right () result <- getFile st SFSender testSenderId @@ -171,7 +171,7 @@ testAckFile = withPgStore $ \st -> do (sndKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g (rcpKey, _) <- atomically $ C.generateAuthKeyPair C.SEd25519 g let fileInfo = testFileInfo sndKey - addFile st testSenderId fileInfo testCreatedAt EntityActive `shouldReturn` Right () + addFile st testSenderId fileInfo testCreatedAt Nothing EntityActive `shouldReturn` Right () addRecipient st testSenderId (FileRecipient testRecipientId rcpKey) `shouldReturn` Right () ackFile st testRecipientId `shouldReturn` Right () -- Recipient gone, but file still exists @@ -189,11 +189,11 @@ testExpiredFiles = withPgStore $ \st -> do oldTime = RoundedSystemTime 100000 newTime = RoundedSystemTime 999999999 -- Add old and new files - addFile st (EntityId "old_file________") fileInfo oldTime EntityActive `shouldReturn` Right () + addFile st (EntityId "old_file________") fileInfo oldTime Nothing EntityActive `shouldReturn` Right () void $ setFilePath st (EntityId "old_file________") "/tmp/old" - addFile st (EntityId "new_file________") fileInfo newTime EntityActive `shouldReturn` Right () + addFile st (EntityId "new_file________") fileInfo newTime Nothing EntityActive `shouldReturn` Right () -- Query expired with cutoff that only catches old file - expired <- expiredFiles st 500000 100 + expired <- expiredFiles st 500000 500000 100 length expired `shouldBe` 1 case expired of [(sId, path, sz)] -> do @@ -222,8 +222,8 @@ testStorageAndCountForStore st = do fileInfoB = fileInfoA {size = 64000} fileA = EntityId "file_a__________" fileB = EntityId "file_b__________" - addFile st fileA fileInfoA testCreatedAt EntityActive `shouldReturn` Right () - addFile st fileB fileInfoB testCreatedAt EntityActive `shouldReturn` Right () + addFile st fileA fileInfoA testCreatedAt Nothing EntityActive `shouldReturn` Right () + addFile st fileB fileInfoB testCreatedAt Nothing EntityActive `shouldReturn` Right () getFileCount st `shouldReturn` 2 getUsedStorage st `shouldReturn` 0 setFilePath st fileA "/tmp/file_a" `shouldReturn` Right () @@ -248,11 +248,11 @@ testMigrationRoundTrip = do sId1 = EntityId "migration_file_1" sId2 = EntityId "migration_file_2" rId1 = EntityId "migration_rcp_1_" - addFile stmStore sId1 fileInfo1 testCreatedAt EntityActive `shouldReturn` Right () + addFile stmStore sId1 fileInfo1 testCreatedAt Nothing EntityActive `shouldReturn` Right () void $ setFilePath stmStore sId1 "/tmp/file1" addRecipient stmStore sId1 (FileRecipient rId1 rcpKey1) `shouldReturn` Right () let testBlockInfo = BlockingInfo {reason = BRSpam, notice = Nothing} - addFile stmStore sId2 fileInfo2 testCreatedAt (EntityBlocked testBlockInfo) `shouldReturn` Right () + addFile stmStore sId2 fileInfo2 testCreatedAt Nothing (EntityBlocked testBlockInfo) `shouldReturn` Right () -- 2. Write to StoreLog sl <- openWriteStoreLog False storeLogPath writeFileStore sl stmStore