From 3e355c002cce059fe3afb45409bb0f34c2d51ebf Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 23 Jul 2026 19:49:55 +0100 Subject: [PATCH] core: parameter for create address command to configure ratchet keys --- bots/api/COMMANDS.md | 7 ++-- bots/src/API/Docs/Commands.hs | 3 +- .../types/typescript/src/commands.ts | 3 +- .../src/simplex_chat/types/_commands.py | 3 +- src/Simplex/Chat/Bot.hs | 2 +- src/Simplex/Chat/Controller.hs | 4 +-- src/Simplex/Chat/Core.hs | 2 +- src/Simplex/Chat/Library/Commands.hs | 18 +++++++---- .../SQLite/Migrations/agent_query_plans.txt | 32 ------------------- 9 files changed, 25 insertions(+), 49 deletions(-) diff --git a/bots/api/COMMANDS.md b/bots/api/COMMANDS.md index 463dd4088b..19569482ce 100644 --- a/bots/api/COMMANDS.md +++ b/bots/api/COMMANDS.md @@ -88,19 +88,20 @@ Create bot address. **Parameters**: - userId: int64 +- pqRatchet: bool? **Syntax**: ``` -/_address +/_address [ pq_ratchet=on|off] ``` ```javascript -'/_address ' + userId // JavaScript +'/_address ' + userId + (typeof pqRatchet == 'boolean' ? ' pq_ratchet=' + (pqRatchet ? 'on' : 'off') : '') // JavaScript ``` ```python -'/_address ' + str(userId) # Python +'/_address ' + str(userId) + ((' pq_ratchet=' + ('on' if pqRatchet else 'off')) if pqRatchet is not None else '') # Python ``` **Responses**: diff --git a/bots/src/API/Docs/Commands.hs b/bots/src/API/Docs/Commands.hs index 46f8b6032d..d4286f8e8a 100644 --- a/bots/src/API/Docs/Commands.hs +++ b/bots/src/API/Docs/Commands.hs @@ -77,7 +77,7 @@ chatCommandsDocsData :: [(String, String, [(ConsName, [String], Text, [ConsName] chatCommandsDocsData = [ ( "Address commands", "Bots can use these commands to automatically check and create address when initialized", - [ ("APICreateMyAddress", ["server_"], "Create bot address.", ["CRUserContactLinkCreated", "CRChatCmdError"], [], Just UNInteractive, "/_address " <> Param "userId"), + [ ("APICreateMyAddress", ["server_"], "Create bot address.", ["CRUserContactLinkCreated", "CRChatCmdError"], [], Just UNInteractive, "/_address " <> Param "userId" <> OnOffParam "pq_ratchet" "pqRatchet" Nothing), ("APIDeleteMyAddress", [], "Delete bot address.", ["CRUserContactLinkDeleted", "CRChatCmdError"], [], Just UNBackground, "/_delete_address " <> Param "userId"), ("APIShowMyAddress", [], "Get bot address and settings.", ["CRUserContactLink", "CRChatCmdError"], [], Nothing, "/_show_address " <> Param "userId"), ("APISetProfileAddress", [], "Add address to bot profile.", ["CRUserProfileUpdated", "CRChatCmdError"], [], Just UNInteractive, "/_profile_address " <> Param "userId" <> " " <> OnOff "enable"), @@ -392,6 +392,7 @@ undocumentedCommands = "APIRejectCall", "APIReorderChatTags", "APIReportMessage", + "APIRotateAddressRatchetKeys", "APISaveAppSettings", "APISendCallAnswer", "APISendCallExtraInfo", diff --git a/packages/simplex-chat-client/types/typescript/src/commands.ts b/packages/simplex-chat-client/types/typescript/src/commands.ts index 3f4e3ad7ee..50965cd2fa 100644 --- a/packages/simplex-chat-client/types/typescript/src/commands.ts +++ b/packages/simplex-chat-client/types/typescript/src/commands.ts @@ -12,13 +12,14 @@ import {CR} from "./responses" // Network usage: interactive. export interface APICreateMyAddress { userId: number // int64 + pqRatchet?: boolean } export namespace APICreateMyAddress { export type Response = CR.UserContactLinkCreated | CR.ChatCmdError export function cmdString(self: APICreateMyAddress): string { - return '/_address ' + self.userId + return '/_address ' + self.userId + (typeof self.pqRatchet == 'boolean' ? ' pq_ratchet=' + (self.pqRatchet ? 'on' : 'off') : '') } } diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_commands.py b/packages/simplex-chat-python/src/simplex_chat/types/_commands.py index a7f4a56465..713cce872b 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_commands.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_commands.py @@ -13,10 +13,11 @@ from . import _responses as CR # Network usage: interactive. class APICreateMyAddress(TypedDict): userId: int # int64 + pqRatchet: NotRequired[bool] def APICreateMyAddress_cmd_string(self: APICreateMyAddress) -> str: - return '/_address ' + str(self['userId']) + return '/_address ' + str(self['userId']) + ((' pq_ratchet=' + ('on' if self.get('pqRatchet') else 'off')) if self.get('pqRatchet') is not None else '') APICreateMyAddress_Response = CR.UserContactLinkCreated | CR.ChatCmdError diff --git a/src/Simplex/Chat/Bot.hs b/src/Simplex/Chat/Bot.hs index 7284b72d62..40ffa4486c 100644 --- a/src/Simplex/Chat/Bot.hs +++ b/src/Simplex/Chat/Bot.hs @@ -56,7 +56,7 @@ initializeBotAddress' logAddress cc = do Left (ChatErrorStore SEUserContactLinkNotFound) -> do when logAddress $ putStrLn "No bot address, creating..." -- TODO [short links] create short link by default - sendChatCmd cc CreateMyAddress >>= \case + sendChatCmd cc (CreateMyAddress Nothing) >>= \case Right (CRUserContactLinkCreated _ ccLink) -> showBotAddress ccLink _ -> putStrLn "can't create bot address" >> exitFailure _ -> putStrLn "unexpected response" >> exitFailure diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 6f75fb2b0b..4b14ca6b5c 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -548,8 +548,8 @@ data ChatCommand | ClearContact ContactName | APIListContacts {userId :: UserId} | ListContacts - | APICreateMyAddress {userId :: UserId, server_ :: Maybe SMPServerWithAuth} - | CreateMyAddress + | APICreateMyAddress {userId :: UserId, server_ :: Maybe SMPServerWithAuth, pqRatchet :: Maybe Bool} + | CreateMyAddress {pqRatchet :: Maybe Bool} | APIDeleteMyAddress {userId :: UserId} | DeleteMyAddress | APIShowMyAddress {userId :: UserId} diff --git a/src/Simplex/Chat/Core.hs b/src/Simplex/Chat/Core.hs index 5bcd381fed..6ba49e223c 100644 --- a/src/Simplex/Chat/Core.hs +++ b/src/Simplex/Chat/Core.hs @@ -175,7 +175,7 @@ askCreateRelayAddress cc@ChatController {chatStore} user@User {userId} server_ h promptCreate = do ok <- if headless then pure True else onOffPrompt "Create relay address" True when ok $ - execChatCommand' (APICreateMyAddress userId server_) 0 `runReaderT` cc >>= \case + execChatCommand' (APICreateMyAddress userId server_ Nothing) 0 `runReaderT` cc >>= \case Right (CRUserContactLinkCreated _ address) -> do putStrLn "Chat relay address is created:" putStrLn $ addressStr address diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index b95e1cea6a..4f1d99cb5d 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -2399,7 +2399,7 @@ processChatCommand cxt nm = \case CRContactsList user <$> withFastStore' (\db -> getUserContacts db cxt user) ListContacts -> withUser $ \User {userId} -> processChatCommand cxt nm $ APIListContacts userId - APICreateMyAddress userId server_ -> withUserId userId $ \user@User {userChatRelay} -> do + APICreateMyAddress userId server_ pqRatchet_ -> withUserId userId $ \user@User {userChatRelay} -> do withFastStore' (\db -> runExceptT $ getUserAddress db user) >>= \case Left SEUserContactLinkNotFound -> pure () Left e -> throwError $ ChatErrorStore e @@ -2408,8 +2408,12 @@ processChatCommand cxt nm = \case gVar <- asks random rootKey@(rootPubKey, rootPrivKey) <- liftIO $ atomically $ C.generateKeyPair gVar let entityId = C.sha256Hash $ C.pubKeyBytes rootPubKey - -- TODO [address DR] switch to IKUsePQ True - (ccLink, preparedParams) <- withAgent $ \a -> prepareConnectionLink a (aUserId user) rootKey entityId True Nothing IKPQOn False server_ + -- TODO [address DR] remove this option and switch to IKUsePQ True + let (pqInitKeys, useDR) = case pqRatchet_ of + Just True -> (IKUsePQ, True) + Just False -> (IKPQOn, True) + Nothing -> (IKPQOn, False) + (ccLink, preparedParams) <- withAgent $ \a -> prepareConnectionLink a (aUserId user) rootKey entityId True Nothing pqInitKeys useDR server_ ccLink' <- shortenCreatedLink ccLink -- TODO [relays] relay: add identity, key to link data? userData <- @@ -2421,8 +2425,8 @@ processChatCommand cxt nm = \case let ccLink'' = if isTrue userChatRelay then setShortLinkType CCTRelay ccLink' else ccLink' withFastStore $ \db -> createUserContactLink db user connId ccLink'' subMode rootPrivKey pure $ CRUserContactLinkCreated user ccLink'' - CreateMyAddress -> withUser $ \User {userId} -> - processChatCommand cxt nm $ APICreateMyAddress userId Nothing + CreateMyAddress ratchetKeys_ -> withUser $ \User {userId} -> + processChatCommand cxt nm $ APICreateMyAddress userId Nothing ratchetKeys_ APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do conn <- withFastStore $ \db -> getUserAddressConnection db cxt user withChatLock "deleteMyAddress" $ do @@ -5698,8 +5702,8 @@ chatCommandP = ("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal), "/_connect contact " *> (APIConnectContactViaAddress <$> A.decimal <*> incognitoOnOffP <* A.space <*> A.decimal), "/simplex" *> (ConnectSimplex <$> incognitoP), - "/_address " *> (APICreateMyAddress <$> A.decimal <*> optional (A.space *> strP)), - ("/address" <|> "/ad") $> CreateMyAddress, + "/_address " *> (APICreateMyAddress <$> A.decimal <*> optional (A.space *> strP) <*> optional (" pq_ratchet=" *> onOffP)), + ("/address" <|> "/ad") *> (CreateMyAddress <$> optional (" pq_ratchet=" *> onOffP)), "/_delete_address " *> (APIDeleteMyAddress <$> A.decimal), ("/delete_address" <|> "/da") $> DeleteMyAddress, "/_show_address " *> (APIShowMyAddress <$> A.decimal), diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt index b79b31c183..47bf904058 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt @@ -559,31 +559,6 @@ Plan: SEARCH s USING PRIMARY KEY (conn_id=? AND internal_snd_id=?) SEARCH m USING PRIMARY KEY (conn_id=? AND internal_id=?) -Query: - SELECT x3dh_priv_key_1, x3dh_priv_key_2, pq_priv_kem - FROM address_ratchet_keys - WHERE conn_id = ? AND ratchet_key_id = ? - -Plan: -SEARCH address_ratchet_keys USING INDEX idx_address_ratchet_keys (conn_id=? AND ratchet_key_id=?) - -Query: - DELETE FROM address_ratchet_keys - WHERE conn_id = ? - AND address_ratchet_key_id NOT IN ( - SELECT address_ratchet_key_id - FROM address_ratchet_keys - WHERE conn_id = ? - ORDER BY address_ratchet_key_id DESC - LIMIT ? - ) - -Plan: -SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?) -LIST SUBQUERY 1 -SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?) -USE TEMP B-TREE FOR ORDER BY - Query: DELETE FROM conn_confirmations WHERE conn_id = ? @@ -591,13 +566,6 @@ Query: Plan: SEARCH conn_confirmations USING COVERING INDEX idx_conn_confirmations_conn_id (conn_id=?) -Query: - INSERT INTO address_ratchet_keys - (conn_id, ratchet_key_id, x3dh_priv_key_1, x3dh_priv_key_2, pq_priv_kem) - VALUES (?, ?, ?, ?, ?) - -Plan: - Query: INSERT INTO connections (user_id, conn_id, conn_mode, smp_agent_version, enable_ntfs, pq_support, duplex_handshake) VALUES (?,?,?,?,?,?,?)