test_event_chain.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. # Copyright 2020 The Matrix.org Foundation C.I.C.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the 'License');
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an 'AS IS' BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import Dict, List, Set, Tuple
  15. from twisted.trial import unittest
  16. from synapse.api.constants import EventTypes
  17. from synapse.api.room_versions import RoomVersions
  18. from synapse.events import EventBase
  19. from synapse.events.snapshot import EventContext
  20. from synapse.rest import admin
  21. from synapse.rest.client import login, room
  22. from synapse.storage.databases.main.events import _LinkMap
  23. from synapse.types import create_requester
  24. from tests.unittest import HomeserverTestCase
  25. class EventChainStoreTestCase(HomeserverTestCase):
  26. def prepare(self, reactor, clock, hs):
  27. self.store = hs.get_datastores().main
  28. self._next_stream_ordering = 1
  29. def test_simple(self):
  30. """Test that the example in `docs/auth_chain_difference_algorithm.md`
  31. works.
  32. """
  33. event_factory = self.hs.get_event_builder_factory()
  34. bob = "@creator:test"
  35. alice = "@alice:test"
  36. room_id = "!room:test"
  37. # Ensure that we have a rooms entry so that we generate the chain index.
  38. self.get_success(
  39. self.store.store_room(
  40. room_id=room_id,
  41. room_creator_user_id="",
  42. is_public=True,
  43. room_version=RoomVersions.V6,
  44. )
  45. )
  46. create = self.get_success(
  47. event_factory.for_room_version(
  48. RoomVersions.V6,
  49. {
  50. "type": EventTypes.Create,
  51. "state_key": "",
  52. "sender": bob,
  53. "room_id": room_id,
  54. "content": {"tag": "create"},
  55. },
  56. ).build(prev_event_ids=[], auth_event_ids=[])
  57. )
  58. bob_join = self.get_success(
  59. event_factory.for_room_version(
  60. RoomVersions.V6,
  61. {
  62. "type": EventTypes.Member,
  63. "state_key": bob,
  64. "sender": bob,
  65. "room_id": room_id,
  66. "content": {"tag": "bob_join"},
  67. },
  68. ).build(prev_event_ids=[], auth_event_ids=[create.event_id])
  69. )
  70. power = self.get_success(
  71. event_factory.for_room_version(
  72. RoomVersions.V6,
  73. {
  74. "type": EventTypes.PowerLevels,
  75. "state_key": "",
  76. "sender": bob,
  77. "room_id": room_id,
  78. "content": {"tag": "power"},
  79. },
  80. ).build(
  81. prev_event_ids=[],
  82. auth_event_ids=[create.event_id, bob_join.event_id],
  83. )
  84. )
  85. alice_invite = self.get_success(
  86. event_factory.for_room_version(
  87. RoomVersions.V6,
  88. {
  89. "type": EventTypes.Member,
  90. "state_key": alice,
  91. "sender": bob,
  92. "room_id": room_id,
  93. "content": {"tag": "alice_invite"},
  94. },
  95. ).build(
  96. prev_event_ids=[],
  97. auth_event_ids=[create.event_id, bob_join.event_id, power.event_id],
  98. )
  99. )
  100. alice_join = self.get_success(
  101. event_factory.for_room_version(
  102. RoomVersions.V6,
  103. {
  104. "type": EventTypes.Member,
  105. "state_key": alice,
  106. "sender": alice,
  107. "room_id": room_id,
  108. "content": {"tag": "alice_join"},
  109. },
  110. ).build(
  111. prev_event_ids=[],
  112. auth_event_ids=[create.event_id, alice_invite.event_id, power.event_id],
  113. )
  114. )
  115. power_2 = self.get_success(
  116. event_factory.for_room_version(
  117. RoomVersions.V6,
  118. {
  119. "type": EventTypes.PowerLevels,
  120. "state_key": "",
  121. "sender": bob,
  122. "room_id": room_id,
  123. "content": {"tag": "power_2"},
  124. },
  125. ).build(
  126. prev_event_ids=[],
  127. auth_event_ids=[create.event_id, bob_join.event_id, power.event_id],
  128. )
  129. )
  130. bob_join_2 = self.get_success(
  131. event_factory.for_room_version(
  132. RoomVersions.V6,
  133. {
  134. "type": EventTypes.Member,
  135. "state_key": bob,
  136. "sender": bob,
  137. "room_id": room_id,
  138. "content": {"tag": "bob_join_2"},
  139. },
  140. ).build(
  141. prev_event_ids=[],
  142. auth_event_ids=[create.event_id, bob_join.event_id, power.event_id],
  143. )
  144. )
  145. alice_join2 = self.get_success(
  146. event_factory.for_room_version(
  147. RoomVersions.V6,
  148. {
  149. "type": EventTypes.Member,
  150. "state_key": alice,
  151. "sender": alice,
  152. "room_id": room_id,
  153. "content": {"tag": "alice_join2"},
  154. },
  155. ).build(
  156. prev_event_ids=[],
  157. auth_event_ids=[
  158. create.event_id,
  159. alice_join.event_id,
  160. power_2.event_id,
  161. ],
  162. )
  163. )
  164. events = [
  165. create,
  166. bob_join,
  167. power,
  168. alice_invite,
  169. alice_join,
  170. bob_join_2,
  171. power_2,
  172. alice_join2,
  173. ]
  174. expected_links = [
  175. (bob_join, create),
  176. (power, create),
  177. (power, bob_join),
  178. (alice_invite, create),
  179. (alice_invite, power),
  180. (alice_invite, bob_join),
  181. (bob_join_2, power),
  182. (alice_join2, power_2),
  183. ]
  184. self.persist(events)
  185. chain_map, link_map = self.fetch_chains(events)
  186. # Check that the expected links and only the expected links have been
  187. # added.
  188. self.assertEqual(len(expected_links), len(list(link_map.get_additions())))
  189. for start, end in expected_links:
  190. start_id, start_seq = chain_map[start.event_id]
  191. end_id, end_seq = chain_map[end.event_id]
  192. self.assertIn(
  193. (start_seq, end_seq), list(link_map.get_links_between(start_id, end_id))
  194. )
  195. # Test that everything can reach the create event, but the create event
  196. # can't reach anything.
  197. for event in events[1:]:
  198. self.assertTrue(
  199. link_map.exists_path_from(
  200. chain_map[event.event_id], chain_map[create.event_id]
  201. ),
  202. )
  203. self.assertFalse(
  204. link_map.exists_path_from(
  205. chain_map[create.event_id],
  206. chain_map[event.event_id],
  207. ),
  208. )
  209. def test_out_of_order_events(self):
  210. """Test that we handle persisting events that we don't have the full
  211. auth chain for yet (which should only happen for out of band memberships).
  212. """
  213. event_factory = self.hs.get_event_builder_factory()
  214. bob = "@creator:test"
  215. alice = "@alice:test"
  216. room_id = "!room:test"
  217. # Ensure that we have a rooms entry so that we generate the chain index.
  218. self.get_success(
  219. self.store.store_room(
  220. room_id=room_id,
  221. room_creator_user_id="",
  222. is_public=True,
  223. room_version=RoomVersions.V6,
  224. )
  225. )
  226. # First persist the base room.
  227. create = self.get_success(
  228. event_factory.for_room_version(
  229. RoomVersions.V6,
  230. {
  231. "type": EventTypes.Create,
  232. "state_key": "",
  233. "sender": bob,
  234. "room_id": room_id,
  235. "content": {"tag": "create"},
  236. },
  237. ).build(prev_event_ids=[], auth_event_ids=[])
  238. )
  239. bob_join = self.get_success(
  240. event_factory.for_room_version(
  241. RoomVersions.V6,
  242. {
  243. "type": EventTypes.Member,
  244. "state_key": bob,
  245. "sender": bob,
  246. "room_id": room_id,
  247. "content": {"tag": "bob_join"},
  248. },
  249. ).build(prev_event_ids=[], auth_event_ids=[create.event_id])
  250. )
  251. power = self.get_success(
  252. event_factory.for_room_version(
  253. RoomVersions.V6,
  254. {
  255. "type": EventTypes.PowerLevels,
  256. "state_key": "",
  257. "sender": bob,
  258. "room_id": room_id,
  259. "content": {"tag": "power"},
  260. },
  261. ).build(
  262. prev_event_ids=[],
  263. auth_event_ids=[create.event_id, bob_join.event_id],
  264. )
  265. )
  266. self.persist([create, bob_join, power])
  267. # Now persist an invite and a couple of memberships out of order.
  268. alice_invite = self.get_success(
  269. event_factory.for_room_version(
  270. RoomVersions.V6,
  271. {
  272. "type": EventTypes.Member,
  273. "state_key": alice,
  274. "sender": bob,
  275. "room_id": room_id,
  276. "content": {"tag": "alice_invite"},
  277. },
  278. ).build(
  279. prev_event_ids=[],
  280. auth_event_ids=[create.event_id, bob_join.event_id, power.event_id],
  281. )
  282. )
  283. alice_join = self.get_success(
  284. event_factory.for_room_version(
  285. RoomVersions.V6,
  286. {
  287. "type": EventTypes.Member,
  288. "state_key": alice,
  289. "sender": alice,
  290. "room_id": room_id,
  291. "content": {"tag": "alice_join"},
  292. },
  293. ).build(
  294. prev_event_ids=[],
  295. auth_event_ids=[create.event_id, alice_invite.event_id, power.event_id],
  296. )
  297. )
  298. alice_join2 = self.get_success(
  299. event_factory.for_room_version(
  300. RoomVersions.V6,
  301. {
  302. "type": EventTypes.Member,
  303. "state_key": alice,
  304. "sender": alice,
  305. "room_id": room_id,
  306. "content": {"tag": "alice_join2"},
  307. },
  308. ).build(
  309. prev_event_ids=[],
  310. auth_event_ids=[create.event_id, alice_join.event_id, power.event_id],
  311. )
  312. )
  313. self.persist([alice_join])
  314. self.persist([alice_join2])
  315. self.persist([alice_invite])
  316. # The end result should be sane.
  317. events = [create, bob_join, power, alice_invite, alice_join]
  318. chain_map, link_map = self.fetch_chains(events)
  319. expected_links = [
  320. (bob_join, create),
  321. (power, create),
  322. (power, bob_join),
  323. (alice_invite, create),
  324. (alice_invite, power),
  325. (alice_invite, bob_join),
  326. ]
  327. # Check that the expected links and only the expected links have been
  328. # added.
  329. self.assertEqual(len(expected_links), len(list(link_map.get_additions())))
  330. for start, end in expected_links:
  331. start_id, start_seq = chain_map[start.event_id]
  332. end_id, end_seq = chain_map[end.event_id]
  333. self.assertIn(
  334. (start_seq, end_seq), list(link_map.get_links_between(start_id, end_id))
  335. )
  336. def persist(
  337. self,
  338. events: List[EventBase],
  339. ):
  340. """Persist the given events and check that the links generated match
  341. those given.
  342. """
  343. persist_events_store = self.hs.get_datastores().persist_events
  344. for e in events:
  345. e.internal_metadata.stream_ordering = self._next_stream_ordering
  346. self._next_stream_ordering += 1
  347. def _persist(txn):
  348. # We need to persist the events to the events and state_events
  349. # tables.
  350. persist_events_store._store_event_txn(
  351. txn,
  352. [(e, EventContext(self.hs.get_storage_controllers())) for e in events],
  353. )
  354. # Actually call the function that calculates the auth chain stuff.
  355. persist_events_store._persist_event_auth_chain_txn(txn, events)
  356. self.get_success(
  357. persist_events_store.db_pool.runInteraction(
  358. "_persist",
  359. _persist,
  360. )
  361. )
  362. def fetch_chains(
  363. self, events: List[EventBase]
  364. ) -> Tuple[Dict[str, Tuple[int, int]], _LinkMap]:
  365. # Fetch the map from event ID -> (chain ID, sequence number)
  366. rows = self.get_success(
  367. self.store.db_pool.simple_select_many_batch(
  368. table="event_auth_chains",
  369. column="event_id",
  370. iterable=[e.event_id for e in events],
  371. retcols=("event_id", "chain_id", "sequence_number"),
  372. keyvalues={},
  373. )
  374. )
  375. chain_map = {
  376. row["event_id"]: (row["chain_id"], row["sequence_number"]) for row in rows
  377. }
  378. # Fetch all the links and pass them to the _LinkMap.
  379. rows = self.get_success(
  380. self.store.db_pool.simple_select_many_batch(
  381. table="event_auth_chain_links",
  382. column="origin_chain_id",
  383. iterable=[chain_id for chain_id, _ in chain_map.values()],
  384. retcols=(
  385. "origin_chain_id",
  386. "origin_sequence_number",
  387. "target_chain_id",
  388. "target_sequence_number",
  389. ),
  390. keyvalues={},
  391. )
  392. )
  393. link_map = _LinkMap()
  394. for row in rows:
  395. added = link_map.add_link(
  396. (row["origin_chain_id"], row["origin_sequence_number"]),
  397. (row["target_chain_id"], row["target_sequence_number"]),
  398. )
  399. # We shouldn't have persisted any redundant links
  400. self.assertTrue(added)
  401. return chain_map, link_map
  402. class LinkMapTestCase(unittest.TestCase):
  403. def test_simple(self):
  404. """Basic tests for the LinkMap."""
  405. link_map = _LinkMap()
  406. link_map.add_link((1, 1), (2, 1), new=False)
  407. self.assertCountEqual(link_map.get_links_between(1, 2), [(1, 1)])
  408. self.assertCountEqual(link_map.get_links_from((1, 1)), [(2, 1)])
  409. self.assertCountEqual(link_map.get_additions(), [])
  410. self.assertTrue(link_map.exists_path_from((1, 5), (2, 1)))
  411. self.assertFalse(link_map.exists_path_from((1, 5), (2, 2)))
  412. self.assertTrue(link_map.exists_path_from((1, 5), (1, 1)))
  413. self.assertFalse(link_map.exists_path_from((1, 1), (1, 5)))
  414. # Attempting to add a redundant link is ignored.
  415. self.assertFalse(link_map.add_link((1, 4), (2, 1)))
  416. self.assertCountEqual(link_map.get_links_between(1, 2), [(1, 1)])
  417. # Adding new non-redundant links works
  418. self.assertTrue(link_map.add_link((1, 3), (2, 3)))
  419. self.assertCountEqual(link_map.get_links_between(1, 2), [(1, 1), (3, 3)])
  420. self.assertTrue(link_map.add_link((2, 5), (1, 3)))
  421. self.assertCountEqual(link_map.get_links_between(2, 1), [(5, 3)])
  422. self.assertCountEqual(link_map.get_links_between(1, 2), [(1, 1), (3, 3)])
  423. self.assertCountEqual(link_map.get_additions(), [(1, 3, 2, 3), (2, 5, 1, 3)])
  424. class EventChainBackgroundUpdateTestCase(HomeserverTestCase):
  425. servlets = [
  426. admin.register_servlets,
  427. room.register_servlets,
  428. login.register_servlets,
  429. ]
  430. def prepare(self, reactor, clock, hs):
  431. self.store = hs.get_datastores().main
  432. self.user_id = self.register_user("foo", "pass")
  433. self.token = self.login("foo", "pass")
  434. self.requester = create_requester(self.user_id)
  435. def _generate_room(self) -> Tuple[str, List[Set[str]]]:
  436. """Insert a room without a chain cover index."""
  437. room_id = self.helper.create_room_as(self.user_id, tok=self.token)
  438. # Mark the room as not having a chain cover index
  439. self.get_success(
  440. self.store.db_pool.simple_update(
  441. table="rooms",
  442. keyvalues={"room_id": room_id},
  443. updatevalues={"has_auth_chain_index": False},
  444. desc="test",
  445. )
  446. )
  447. # Create a fork in the DAG with different events.
  448. event_handler = self.hs.get_event_creation_handler()
  449. latest_event_ids = self.get_success(
  450. self.store.get_prev_events_for_room(room_id)
  451. )
  452. event, context = self.get_success(
  453. event_handler.create_event(
  454. self.requester,
  455. {
  456. "type": "some_state_type",
  457. "state_key": "",
  458. "content": {},
  459. "room_id": room_id,
  460. "sender": self.user_id,
  461. },
  462. prev_event_ids=latest_event_ids,
  463. )
  464. )
  465. self.get_success(
  466. event_handler.handle_new_client_event(self.requester, event, context)
  467. )
  468. state1 = set(self.get_success(context.get_current_state_ids()).values())
  469. event, context = self.get_success(
  470. event_handler.create_event(
  471. self.requester,
  472. {
  473. "type": "some_state_type",
  474. "state_key": "",
  475. "content": {},
  476. "room_id": room_id,
  477. "sender": self.user_id,
  478. },
  479. prev_event_ids=latest_event_ids,
  480. )
  481. )
  482. self.get_success(
  483. event_handler.handle_new_client_event(self.requester, event, context)
  484. )
  485. state2 = set(self.get_success(context.get_current_state_ids()).values())
  486. # Delete the chain cover info.
  487. def _delete_tables(txn):
  488. txn.execute("DELETE FROM event_auth_chains")
  489. txn.execute("DELETE FROM event_auth_chain_links")
  490. self.get_success(self.store.db_pool.runInteraction("test", _delete_tables))
  491. return room_id, [state1, state2]
  492. def test_background_update_single_room(self):
  493. """Test that the background update to calculate auth chains for historic
  494. rooms works correctly.
  495. """
  496. # Create a room
  497. room_id, states = self._generate_room()
  498. # Insert and run the background update.
  499. self.get_success(
  500. self.store.db_pool.simple_insert(
  501. "background_updates",
  502. {"update_name": "chain_cover", "progress_json": "{}"},
  503. )
  504. )
  505. # Ugh, have to reset this flag
  506. self.store.db_pool.updates._all_done = False
  507. self.wait_for_background_updates()
  508. # Test that the `has_auth_chain_index` has been set
  509. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id)))
  510. # Test that calculating the auth chain difference using the newly
  511. # calculated chain cover works.
  512. self.get_success(
  513. self.store.db_pool.runInteraction(
  514. "test",
  515. self.store._get_auth_chain_difference_using_cover_index_txn,
  516. room_id,
  517. states,
  518. )
  519. )
  520. def test_background_update_multiple_rooms(self):
  521. """Test that the background update to calculate auth chains for historic
  522. rooms works correctly.
  523. """
  524. # Create a room
  525. room_id1, states1 = self._generate_room()
  526. room_id2, states2 = self._generate_room()
  527. room_id3, states2 = self._generate_room()
  528. # Insert and run the background update.
  529. self.get_success(
  530. self.store.db_pool.simple_insert(
  531. "background_updates",
  532. {"update_name": "chain_cover", "progress_json": "{}"},
  533. )
  534. )
  535. # Ugh, have to reset this flag
  536. self.store.db_pool.updates._all_done = False
  537. self.wait_for_background_updates()
  538. # Test that the `has_auth_chain_index` has been set
  539. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id1)))
  540. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id2)))
  541. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id3)))
  542. # Test that calculating the auth chain difference using the newly
  543. # calculated chain cover works.
  544. self.get_success(
  545. self.store.db_pool.runInteraction(
  546. "test",
  547. self.store._get_auth_chain_difference_using_cover_index_txn,
  548. room_id1,
  549. states1,
  550. )
  551. )
  552. def test_background_update_single_large_room(self):
  553. """Test that the background update to calculate auth chains for historic
  554. rooms works correctly.
  555. """
  556. # Create a room
  557. room_id, states = self._generate_room()
  558. # Add a bunch of state so that it takes multiple iterations of the
  559. # background update to process the room.
  560. for i in range(0, 150):
  561. self.helper.send_state(
  562. room_id, event_type="m.test", body={"index": i}, tok=self.token
  563. )
  564. # Insert and run the background update.
  565. self.get_success(
  566. self.store.db_pool.simple_insert(
  567. "background_updates",
  568. {"update_name": "chain_cover", "progress_json": "{}"},
  569. )
  570. )
  571. # Ugh, have to reset this flag
  572. self.store.db_pool.updates._all_done = False
  573. iterations = 0
  574. while not self.get_success(
  575. self.store.db_pool.updates.has_completed_background_updates()
  576. ):
  577. iterations += 1
  578. self.get_success(
  579. self.store.db_pool.updates.do_next_background_update(False), by=0.1
  580. )
  581. # Ensure that we did actually take multiple iterations to process the
  582. # room.
  583. self.assertGreater(iterations, 1)
  584. # Test that the `has_auth_chain_index` has been set
  585. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id)))
  586. # Test that calculating the auth chain difference using the newly
  587. # calculated chain cover works.
  588. self.get_success(
  589. self.store.db_pool.runInteraction(
  590. "test",
  591. self.store._get_auth_chain_difference_using_cover_index_txn,
  592. room_id,
  593. states,
  594. )
  595. )
  596. def test_background_update_multiple_large_room(self):
  597. """Test that the background update to calculate auth chains for historic
  598. rooms works correctly.
  599. """
  600. # Create the rooms
  601. room_id1, _ = self._generate_room()
  602. room_id2, _ = self._generate_room()
  603. # Add a bunch of state so that it takes multiple iterations of the
  604. # background update to process the room.
  605. for i in range(0, 150):
  606. self.helper.send_state(
  607. room_id1, event_type="m.test", body={"index": i}, tok=self.token
  608. )
  609. for i in range(0, 150):
  610. self.helper.send_state(
  611. room_id2, event_type="m.test", body={"index": i}, tok=self.token
  612. )
  613. # Insert and run the background update.
  614. self.get_success(
  615. self.store.db_pool.simple_insert(
  616. "background_updates",
  617. {"update_name": "chain_cover", "progress_json": "{}"},
  618. )
  619. )
  620. # Ugh, have to reset this flag
  621. self.store.db_pool.updates._all_done = False
  622. iterations = 0
  623. while not self.get_success(
  624. self.store.db_pool.updates.has_completed_background_updates()
  625. ):
  626. iterations += 1
  627. self.get_success(
  628. self.store.db_pool.updates.do_next_background_update(False), by=0.1
  629. )
  630. # Ensure that we did actually take multiple iterations to process the
  631. # room.
  632. self.assertGreater(iterations, 1)
  633. # Test that the `has_auth_chain_index` has been set
  634. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id1)))
  635. self.assertTrue(self.get_success(self.store.has_auth_chain_index(room_id2)))