Skip to content

Commit c5eb660

Browse files
authored
agent: allow to accept contact requests after address is deleted (#1580)
1 parent a46edd6 commit c5eb660

File tree

11 files changed

+136
-67
lines changed

11 files changed

+136
-67
lines changed

simplexmq.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ library
162162
Simplex.Messaging.Agent.Store.Postgres.Migrations.M20241210_initial
163163
Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250203_msg_bodies
164164
Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250322_short_links
165+
Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250702_conn_invitations_remove_cascade_delete
165166
else
166167
exposed-modules:
167168
Simplex.Messaging.Agent.Store.SQLite
@@ -208,6 +209,7 @@ library
208209
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241224_ratchet_e2e_snd_params
209210
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250203_msg_bodies
210211
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250322_short_links
212+
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250702_conn_invitations_remove_cascade_delete
211213
if !flag(client_library)
212214
exposed-modules:
213215
Simplex.FileTransfer.Client.Main
@@ -559,7 +561,7 @@ test-suite simplexmq-test
559561
build-depends:
560562
memory
561563
, sqlcipher-simple
562-
if !flag(client_postgres) || flag(server_postgres)
564+
if !flag(client_postgres) || flag(client_postgres) || flag(server_postgres)
563565
build-depends:
564566
deepseq ==1.4.*
565567
, process

src/Simplex/Messaging/Agent.hs

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ allowConnectionAsync c = withAgentEnv c .:: allowConnectionAsync' c
343343
{-# INLINE allowConnectionAsync #-}
344344

345345
-- | Accept contact after REQ notification (ACPT command) asynchronously, synchronous response is new connection id
346-
acceptContactAsync :: AgentClient -> ACorrId -> Bool -> ConfirmationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AE ConnId
347-
acceptContactAsync c aCorrId enableNtfs = withAgentEnv c .:: acceptContactAsync' c aCorrId enableNtfs
346+
acceptContactAsync :: AgentClient -> UserId -> ACorrId -> Bool -> ConfirmationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AE ConnId
347+
acceptContactAsync c userId aCorrId enableNtfs = withAgentEnv c .:: acceptContactAsync' c userId aCorrId enableNtfs
348348
{-# INLINE acceptContactAsync #-}
349349

350350
-- | Acknowledge message (ACK command) asynchronously, no synchronous response
@@ -406,8 +406,8 @@ prepareConnectionToJoin c userId enableNtfs = withAgentEnv c .: newConnToJoin c
406406
{-# INLINE prepareConnectionToJoin #-}
407407

408408
-- | Create SMP agent connection without queue (to be joined with acceptContact passing invitation ID).
409-
prepareConnectionToAccept :: AgentClient -> Bool -> ConfirmationId -> PQSupport -> AE ConnId
410-
prepareConnectionToAccept c enableNtfs = withAgentEnv c .: newConnToAccept c "" enableNtfs
409+
prepareConnectionToAccept :: AgentClient -> UserId -> Bool -> ConfirmationId -> PQSupport -> AE ConnId
410+
prepareConnectionToAccept c userId enableNtfs = withAgentEnv c .: newConnToAccept c userId "" enableNtfs
411411
{-# INLINE prepareConnectionToAccept #-}
412412

413413
-- | Join SMP agent connection (JOIN command).
@@ -421,13 +421,13 @@ allowConnection c = withAgentEnv c .:. allowConnection' c
421421
{-# INLINE allowConnection #-}
422422

423423
-- | Accept contact after REQ notification (ACPT command)
424-
acceptContact :: AgentClient -> ConnId -> Bool -> ConfirmationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AE (SndQueueSecured, Maybe ClientServiceId)
425-
acceptContact c connId enableNtfs = withAgentEnv c .:: acceptContact' c connId enableNtfs
424+
acceptContact :: AgentClient -> UserId -> ConnId -> Bool -> ConfirmationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AE (SndQueueSecured, Maybe ClientServiceId)
425+
acceptContact c userId connId enableNtfs = withAgentEnv c .:: acceptContact' c userId connId enableNtfs
426426
{-# INLINE acceptContact #-}
427427

428428
-- | Reject contact (RJCT command)
429-
rejectContact :: AgentClient -> ConnId -> ConfirmationId -> AE ()
430-
rejectContact c = withAgentEnv c .: rejectContact' c
429+
rejectContact :: AgentClient -> ConfirmationId -> AE ()
430+
rejectContact c = withAgentEnv c . rejectContact' c
431431
{-# INLINE rejectContact #-}
432432

433433
-- | Subscribe to receive connection messages (SUB command)
@@ -770,16 +770,13 @@ allowConnectionAsync' c corrId connId confId ownConnInfo =
770770
-- and also it can't be triggered by user concurrently several times in a row. It could be improved similarly to
771771
-- `acceptContact` by creating a new map for invitation locks and taking lock here, and removing `unacceptInvitation`
772772
-- while marking invitation as accepted inside "lock level transaction" after successful `joinConnAsync`.
773-
acceptContactAsync' :: AgentClient -> ACorrId -> Bool -> InvitationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AM ConnId
774-
acceptContactAsync' c corrId enableNtfs invId ownConnInfo pqSupport subMode = do
775-
Invitation {contactConnId, connReq} <- withStore c $ \db -> getInvitation db "acceptContactAsync'" invId
776-
withStore c (`getConn` contactConnId) >>= \case
777-
SomeConn _ (ContactConnection ConnData {userId} _) -> do
778-
withStore' c $ \db -> acceptInvitation db invId ownConnInfo
779-
joinConnAsync c userId corrId enableNtfs connReq ownConnInfo pqSupport subMode `catchAgentError` \err -> do
780-
withStore' c (`unacceptInvitation` invId)
781-
throwE err
782-
_ -> throwE $ CMD PROHIBITED "acceptContactAsync"
773+
acceptContactAsync' :: AgentClient -> UserId -> ACorrId -> Bool -> InvitationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AM ConnId
774+
acceptContactAsync' c userId corrId enableNtfs invId ownConnInfo pqSupport subMode = do
775+
Invitation {connReq} <- withStore c $ \db -> getInvitation db "acceptContactAsync'" invId
776+
withStore' c $ \db -> acceptInvitation db invId ownConnInfo
777+
joinConnAsync c userId corrId enableNtfs connReq ownConnInfo pqSupport subMode `catchAgentError` \err -> do
778+
withStore' c (`unacceptInvitation` invId)
779+
throwE err
783780

784781
ackMessageAsync' :: AgentClient -> ACorrId -> ConnId -> AgentMsgId -> Maybe MsgReceiptInfo -> AM ()
785782
ackMessageAsync' c corrId connId msgId rcptInfo_ = do
@@ -1036,13 +1033,10 @@ newConnToJoin c userId connId enableNtfs cReq pqSup = case cReq of
10361033
cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqSupport}
10371034
withStore c $ \db -> createNewConn db g cData SCMInvitation
10381035

1039-
newConnToAccept :: AgentClient -> ConnId -> Bool -> ConfirmationId -> PQSupport -> AM ConnId
1040-
newConnToAccept c connId enableNtfs invId pqSup = do
1041-
Invitation {connReq, contactConnId} <- withStore c $ \db -> getInvitation db "newConnToAccept" invId
1042-
withStore c (`getConn` contactConnId) >>= \case
1043-
SomeConn _ (ContactConnection ConnData {userId} _) ->
1044-
newConnToJoin c userId connId enableNtfs connReq pqSup
1045-
_ -> throwE $ CMD PROHIBITED "newConnToAccept"
1036+
newConnToAccept :: AgentClient -> UserId -> ConnId -> Bool -> ConfirmationId -> PQSupport -> AM ConnId
1037+
newConnToAccept c userId connId enableNtfs invId pqSup = do
1038+
Invitation {connReq} <- withStore c $ \db -> getInvitation db "newConnToAccept" invId
1039+
newConnToJoin c userId connId enableNtfs connReq pqSup
10461040

10471041
joinConn :: AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQSupport -> SubscriptionMode -> AM (SndQueueSecured, Maybe ClientServiceId)
10481042
joinConn c userId connId enableNtfs cReq cInfo pqSupport subMode = do
@@ -1220,20 +1214,17 @@ allowConnection' c connId confId ownConnInfo = withConnLock c connId "allowConne
12201214
_ -> throwE $ CMD PROHIBITED "allowConnection"
12211215

12221216
-- | Accept contact (ACPT command) in Reader monad
1223-
acceptContact' :: AgentClient -> ConnId -> Bool -> InvitationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AM (SndQueueSecured, Maybe ClientServiceId)
1224-
acceptContact' c connId enableNtfs invId ownConnInfo pqSupport subMode = withConnLock c connId "acceptContact" $ do
1225-
Invitation {contactConnId, connReq} <- withStore c $ \db -> getInvitation db "acceptContact'" invId
1226-
withStore c (`getConn` contactConnId) >>= \case
1227-
SomeConn _ (ContactConnection ConnData {userId} _) -> do
1228-
r <- joinConn c userId connId enableNtfs connReq ownConnInfo pqSupport subMode
1229-
withStore' c $ \db -> acceptInvitation db invId ownConnInfo
1230-
pure r
1231-
_ -> throwE $ CMD PROHIBITED "acceptContact"
1217+
acceptContact' :: AgentClient -> UserId -> ConnId -> Bool -> InvitationId -> ConnInfo -> PQSupport -> SubscriptionMode -> AM (SndQueueSecured, Maybe ClientServiceId)
1218+
acceptContact' c userId connId enableNtfs invId ownConnInfo pqSupport subMode = withConnLock c connId "acceptContact" $ do
1219+
Invitation {connReq} <- withStore c $ \db -> getInvitation db "acceptContact'" invId
1220+
r <- joinConn c userId connId enableNtfs connReq ownConnInfo pqSupport subMode
1221+
withStore' c $ \db -> acceptInvitation db invId ownConnInfo
1222+
pure r
12321223

12331224
-- | Reject contact (RJCT command) in Reader monad
1234-
rejectContact' :: AgentClient -> ConnId -> InvitationId -> AM ()
1235-
rejectContact' c contactConnId invId =
1236-
withStore c $ \db -> deleteInvitation db contactConnId invId
1225+
rejectContact' :: AgentClient -> InvitationId -> AM ()
1226+
rejectContact' c invId =
1227+
withStore' c $ \db -> deleteInvitation db invId
12371228
{-# INLINE rejectContact' #-}
12381229

12391230
-- | Subscribe to receive connection messages (SUB command) in Reader monad

src/Simplex/Messaging/Agent/Store.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ data NewInvitation = NewInvitation
515515

516516
data Invitation = Invitation
517517
{ invitationId :: InvitationId,
518-
contactConnId :: ConnId,
518+
contactConnId_ :: Maybe ConnId,
519519
connReq :: ConnectionRequestUri 'CMInvitation,
520520
recipientConnInfo :: ConnInfo,
521521
ownConnInfo :: Maybe ConnInfo,

src/Simplex/Messaging/Agent/Store/AgentStore.hs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ createInvitation db gVar NewInvitation {contactConnId, connReq, recipientConnInf
728728
db
729729
[sql|
730730
INSERT INTO conn_invitations
731-
(invitation_id, contact_conn_id, cr_invitation, recipient_conn_info, accepted) VALUES (?, ?, ?, ?, 0);
731+
(invitation_id, contact_conn_id, cr_invitation, recipient_conn_info, accepted) VALUES (?, ?, ?, ?, 0);
732732
|]
733733
(Binary invitationId, contactConnId, connReq, Binary recipientConnInfo)
734734

@@ -745,8 +745,8 @@ getInvitation db cxt invitationId =
745745
|]
746746
(Only (Binary invitationId))
747747
where
748-
invitation (contactConnId, connReq, recipientConnInfo, ownConnInfo, BI accepted) =
749-
Invitation {invitationId, contactConnId, connReq, recipientConnInfo, ownConnInfo, accepted}
748+
invitation (contactConnId_, connReq, recipientConnInfo, ownConnInfo, BI accepted) =
749+
Invitation {invitationId, contactConnId_, connReq, recipientConnInfo, ownConnInfo, accepted}
750750

751751
acceptInvitation :: DB.Connection -> InvitationId -> ConnInfo -> IO ()
752752
acceptInvitation db invitationId ownConnInfo =
@@ -764,12 +764,9 @@ unacceptInvitation :: DB.Connection -> InvitationId -> IO ()
764764
unacceptInvitation db invitationId =
765765
DB.execute db "UPDATE conn_invitations SET accepted = 0, own_conn_info = NULL WHERE invitation_id = ?" (Only (Binary invitationId))
766766

767-
deleteInvitation :: DB.Connection -> ConnId -> InvitationId -> IO (Either StoreError ())
768-
deleteInvitation db contactConnId invId =
769-
getConn db contactConnId $>>= \case
770-
SomeConn SCContact _ ->
771-
Right <$> DB.execute db "DELETE FROM conn_invitations WHERE contact_conn_id = ? AND invitation_id = ?" (contactConnId, Binary invId)
772-
_ -> pure $ Left SEConnNotFound
767+
deleteInvitation :: DB.Connection -> InvitationId -> IO ()
768+
deleteInvitation db invId =
769+
DB.execute db "DELETE FROM conn_invitations WHERE invitation_id = ?" (Only (Binary invId))
773770

774771
getInvShortLink :: DB.Connection -> SMPServer -> LinkId -> IO (Maybe InvShortLink)
775772
getInvShortLink db server linkId =

src/Simplex/Messaging/Agent/Store/Postgres/Migrations/App.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import Data.Text (Text)
77
import Simplex.Messaging.Agent.Store.Postgres.Migrations.M20241210_initial
88
import Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250203_msg_bodies
99
import Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250322_short_links
10+
import Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250702_conn_invitations_remove_cascade_delete
1011
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
1112

1213
schemaMigrations :: [(String, Text, Maybe Text)]
1314
schemaMigrations =
1415
[ ("20241210_initial", m20241210_initial, Nothing),
1516
("20250203_msg_bodies", m20250203_msg_bodies, Just down_m20250203_msg_bodies),
16-
("20250322_short_links", m20250322_short_links, Just down_m20250322_short_links)
17+
("20250322_short_links", m20250322_short_links, Just down_m20250322_short_links),
18+
("20250702_conn_invitations_remove_cascade_delete", m20250702_conn_invitations_remove_cascade_delete, Just down_m20250702_conn_invitations_remove_cascade_delete)
1719
]
1820

1921
-- | The list of migrations in ascending order by date
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{-# LANGUAGE QuasiQuotes #-}
2+
3+
module Simplex.Messaging.Agent.Store.Postgres.Migrations.M20250702_conn_invitations_remove_cascade_delete where
4+
5+
import Data.Text (Text)
6+
import qualified Data.Text as T
7+
import Text.RawString.QQ (r)
8+
9+
m20250702_conn_invitations_remove_cascade_delete :: Text
10+
m20250702_conn_invitations_remove_cascade_delete =
11+
T.pack
12+
[r|
13+
ALTER TABLE conn_invitations DROP CONSTRAINT conn_invitations_contact_conn_id_fkey;
14+
15+
ALTER TABLE conn_invitations ALTER COLUMN contact_conn_id DROP NOT NULL;
16+
17+
ALTER TABLE conn_invitations
18+
ADD CONSTRAINT conn_invitations_contact_conn_id_fkey
19+
FOREIGN KEY (contact_conn_id)
20+
REFERENCES connections(conn_id)
21+
ON DELETE SET NULL;
22+
|]
23+
24+
down_m20250702_conn_invitations_remove_cascade_delete :: Text
25+
down_m20250702_conn_invitations_remove_cascade_delete =
26+
T.pack
27+
[r|
28+
ALTER TABLE conn_invitations DROP CONSTRAINT conn_invitations_contact_conn_id_fkey;
29+
30+
ALTER TABLE conn_invitations ALTER COLUMN contact_conn_id SET NOT NULL;
31+
32+
ALTER TABLE conn_invitations
33+
ADD CONSTRAINT conn_invitations_contact_conn_id_fkey
34+
FOREIGN KEY (contact_conn_id)
35+
REFERENCES connections(conn_id)
36+
ON DELETE CASCADE;
37+
|]

src/Simplex/Messaging/Agent/Store/SQLite/Migrations/App.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241007_rcv_queues_last
4343
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241224_ratchet_e2e_snd_params
4444
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250203_msg_bodies
4545
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250322_short_links
46+
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250702_conn_invitations_remove_cascade_delete
4647
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
4748

4849
schemaMigrations :: [(String, Query, Maybe Query)]
@@ -85,7 +86,8 @@ schemaMigrations =
8586
("m20241007_rcv_queues_last_broker_ts", m20241007_rcv_queues_last_broker_ts, Just down_m20241007_rcv_queues_last_broker_ts),
8687
("m20241224_ratchet_e2e_snd_params", m20241224_ratchet_e2e_snd_params, Just down_m20241224_ratchet_e2e_snd_params),
8788
("m20250203_msg_bodies", m20250203_msg_bodies, Just down_m20250203_msg_bodies),
88-
("m20250322_short_links", m20250322_short_links, Just down_m20250322_short_links)
89+
("m20250322_short_links", m20250322_short_links, Just down_m20250322_short_links),
90+
("m20250702_conn_invitations_remove_cascade_delete", m20250702_conn_invitations_remove_cascade_delete, Just down_m20250702_conn_invitations_remove_cascade_delete)
8991
]
9092

9193
-- | The list of migrations in ascending order by date

src/Simplex/Messaging/Agent/Store/SQLite/Migrations/M20250517_service_certs.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250517_service_certs w
55
import Database.SQLite.Simple (Query)
66
import Database.SQLite.Simple.QQ (sql)
77

8+
-- TODO move date forward, create migration for postgres
89
m20250517_service_certs :: Query
910
m20250517_service_certs =
1011
[sql|
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{-# LANGUAGE QuasiQuotes #-}
2+
3+
module Simplex.Messaging.Agent.Store.SQLite.Migrations.M20250702_conn_invitations_remove_cascade_delete where
4+
5+
import Database.SQLite.Simple (Query)
6+
import Database.SQLite.Simple.QQ (sql)
7+
8+
m20250702_conn_invitations_remove_cascade_delete :: Query
9+
m20250702_conn_invitations_remove_cascade_delete =
10+
[sql|
11+
PRAGMA writable_schema=1;
12+
13+
UPDATE sqlite_master
14+
SET sql = replace(
15+
sql,
16+
'contact_conn_id BLOB NOT NULL REFERENCES connections ON DELETE CASCADE',
17+
'contact_conn_id BLOB REFERENCES connections ON DELETE SET NULL'
18+
)
19+
WHERE name = 'conn_invitations' AND type = 'table';
20+
21+
PRAGMA writable_schema=0;
22+
|]
23+
24+
down_m20250702_conn_invitations_remove_cascade_delete :: Query
25+
down_m20250702_conn_invitations_remove_cascade_delete =
26+
[sql|
27+
PRAGMA writable_schema=1;
28+
29+
UPDATE sqlite_master
30+
SET sql = replace(
31+
sql,
32+
'contact_conn_id BLOB REFERENCES connections ON DELETE SET NULL',
33+
'contact_conn_id BLOB NOT NULL REFERENCES connections ON DELETE CASCADE'
34+
)
35+
WHERE name = 'conn_invitations' AND type = 'table';
36+
37+
PRAGMA writable_schema=0;
38+
|]

src/Simplex/Messaging/Agent/Store/SQLite/Migrations/agent_schema.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ CREATE TABLE conn_confirmations(
154154
) WITHOUT ROWID;
155155
CREATE TABLE conn_invitations(
156156
invitation_id BLOB NOT NULL PRIMARY KEY,
157-
contact_conn_id BLOB NOT NULL REFERENCES connections ON DELETE CASCADE,
157+
contact_conn_id BLOB REFERENCES connections ON DELETE SET NULL,
158158
cr_invitation BLOB NOT NULL,
159159
recipient_conn_info BLOB NOT NULL,
160160
accepted INTEGER NOT NULL DEFAULT 0,

0 commit comments

Comments
 (0)