-
Notifications
You must be signed in to change notification settings - Fork 74
add child resolution & ice to CursorStore trait
#2869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
This PR is part of a stack of 12 bookmarks:
Created with jj-stack |
|
Claude finished @insipx's task —— View job PR Review - December 11, 2025Critical Issues1. False positive in The method uses pub fn is_child_of(&self, cursor: &Cursor) -> bool {
self.depends_on.contains_key(&cursor.originator_id)
&& self.depends_on.get(&cursor.originator_id) == cursor.sequence_id
}If an orphan has no dependency for a given originator, Fix: Check key existence explicitly: pub fn is_child_of(&self, cursor: &Cursor) -> bool {
self.depends_on.get(&cursor.originator_id) == cursor.sequence_id
&& self.depends_on.contains_key(&cursor.originator_id)
}2. SQL error in The query references Test Coverage Concerns
Additional Issues3. Missing test for sequence_id=0 edge case Given the false positive bug, there should be explicit tests covering:
4. The HashMap → BTreeMap change is sensible for deterministic ordering and Hash derivation. The new Recommendations
Review completed at 2025-12-11 21:04 UTC |
| #[derive(Default, Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)] | ||
| pub struct GlobalCursor { | ||
| inner: HashMap<OriginatorId, SequenceId>, | ||
| inner: BTreeMap<OriginatorId, SequenceId>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed the backing data structure from a HashMap to a BTreeMap. The BTreeMap makes more sense to me, since we can order the map by keys and also derive Hash on it. the semantics of inserting/"contains" etc. is unchanged
71ae3cb to
d0c6474
Compare
3fa421b to
1829d0a
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2869 +/- ##
==========================================
- Coverage 73.67% 73.49% -0.18%
==========================================
Files 402 402
Lines 51098 51237 +139
==========================================
+ Hits 37644 37656 +12
- Misses 13454 13581 +127 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
1829d0a to
47c44da
Compare
d0c6474 to
2a525e0
Compare
2a525e0 to
4aa9e3f
Compare
47c44da to
a81a59b
Compare
a81a59b to
da907f5
Compare
40bd8fd to
4937bec
Compare
da907f5 to
c5e17ad
Compare
4937bec to
87e1f12
Compare
c5e17ad to
4f2808e
Compare
87e1f12 to
820c628
Compare
4f2808e to
80f2a62
Compare
7456421 to
c7928f5
Compare
74f8e3d to
411c071
Compare
c7928f5 to
32e6075
Compare
411c071 to
7673dfb
Compare
32e6075 to
763c26d
Compare
0160848 to
17e7833
Compare
12512c0 to
2e0df41
Compare
058f33b to
e05e925
Compare
2e0df41 to
c49f3ba
Compare
c49f3ba to
e3540d3
Compare
CursorStore trait
e3540d3 to
7a08a24
Compare
CursorStore traitCursorStore trait
e626807 to
35ec6e7
Compare
35ec6e7 to
0972aaf
Compare
<!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will post its summary as a comment. --> ### Add child resolution and ICE handling by extending `CursorStore` with `ice` and `resolve_children` across in-memory, SQL, and wrapper implementations Extend `CursorStore` with `ice` and `resolve_children`, add an in-memory icebox in `InMemoryCursorStore`, delegate persistence/lookup in the SQLite store, and switch `GlobalCursor` to `BTreeMap` with merge via `apply`. Update callers to construct and return `GlobalCursor` directly. #### 📍Where to Start Start with the trait changes in [cursor_store.rs](https://github.com/xmtp/libxmtp/pull/2869/files#diff-028daad07b57a99602b70e297432f143b84633ff46fa3f2b9851f6b476260eae), then review the in-memory implementation in [in_memory_cursor_store.rs](https://github.com/xmtp/libxmtp/pull/2869/files#diff-81e05c4cbc4c5877da996a1302449b85dde8e85a96338b80953cbc520363c6c2) and the SQLite store in [cursor_store.rs](https://github.com/xmtp/libxmtp/pull/2869/files#diff-92939812065b708096075a08e83053a96bbb0d48d2a13d665eba3336c85740ca). ---- <!-- Macroscope's review summary starts here --> <details> <summary>📊 <a href="https://app.macroscope.com">Macroscope</a> summarized 0972aaf. 9 files reviewed, 6 issues evaluated, 6 issues filtered, 0 comments posted</summary> ### 🗂️ Filtered Issues <details> <summary>xmtp_db/src/encrypted_store/refresh_state.rs — 0 comments posted, 2 evaluated, 2 filtered</summary> - [line 133](https://github.com/xmtp/libxmtp/blob/0972aaf951c6484cd66aee375a5c8819c1079685/xmtp_db/src/encrypted_store/refresh_state.rs#L133): rows_to_global_cursor_map performs unchecked signed-to-unsigned integer casts that can silently wrap negative database values, yielding incorrect OriginatorId and SequenceId. Specifically, `originator_id as u32` and `sequence_id.unwrap_or(0) as u64` will wrap if `originator_id` or `sequence_id` are negative, producing large unsigned values instead of rejecting invalid input. This can corrupt the resulting `GlobalCursor` (e.g., mapping to the wrong originator or an enormous sequence), leading to incorrect cursor computations downstream. Validate non-negativity before casting, or return an error on invalid rows. <b>[ Previously rejected ]</b> - [line 706](https://github.com/xmtp/libxmtp/blob/0972aaf951c6484cd66aee375a5c8819c1079685/xmtp_db/src/encrypted_store/refresh_state.rs#L706): latest_cursor_combined may generate invalid SQL when `entities` contains no kinds that map to `group_messages` (i.e., neither `EntityKind::ApplicationMessage` nor `EntityKind::CommitMessage`). In that case `group_message_kinds` becomes an empty Vec, producing `kind IN ()` in the constructed SQL, which is a syntax error in SQLite. This is reachable if callers pass, e.g., only `EntityKind::Welcome`. Ensure you guard against an empty `group_message_kinds` (e.g., omit the `group_messages` branch or use a false predicate) before constructing the placeholders. <b>[ Out of scope ]</b> </details> <details> <summary>xmtp_mls/src/cursor_store.rs — 0 comments posted, 4 evaluated, 4 filtered</summary> - [line 70](https://github.com/xmtp/libxmtp/blob/0972aaf951c6484cd66aee375a5c8819c1079685/xmtp_mls/src/cursor_store.rs#L70): `sid` from the database is cast to `u64` with `as` without validating non-negativity. If `sid` is negative (e.g., due to DB corruption or unexpected input), it will wrap to a very large `u64`, corrupting the cursor and causing incorrect behavior (e.g., dominance/missing comparisons). Validate `sid >= 0` and return an error or clamp, or change the DB/API to return an unsigned type. <b>[ Previously rejected ]</b> - [line 101](https://github.com/xmtp/libxmtp/blob/0972aaf951c6484cd66aee375a5c8819c1079685/xmtp_mls/src/cursor_store.rs#L101): `sid` is cast to `u64` without validating non-negativity in `latest_per_originator` for identity updates, which can wrap negatives to huge values and corrupt cursor semantics. Validate or use an unsigned type. <b>[ Previously rejected ]</b> - [line 163](https://github.com/xmtp/libxmtp/blob/0972aaf951c6484cd66aee375a5c8819c1079685/xmtp_mls/src/cursor_store.rs#L163): Potential integer wrap: `sid` from `get_latest_sequence_id_for_inbox(...)` is cast with `as u64` without validation. If `sid` is negative, the cast will wrap to a very large `u64`, producing an incorrect `GlobalCursor` value and potentially causing downstream logic to skip messages. Validate that `sid >= 0` and either clamp, normalize, or return an error before inserting into the `GlobalCursor`. Affected line: `map.insert(Originators::INBOX_LOG, sid as u64);`. <b>[ Previously rejected ]</b> - [line 163](https://github.com/xmtp/libxmtp/blob/0972aaf951c6484cd66aee375a5c8819c1079685/xmtp_mls/src/cursor_store.rs#L163): `sid` is cast to `u64` without validation in `latest_for_topics` for `IdentityUpdatesV1`, which can wrap negative SIDs and produce incorrect `GlobalCursor` values. Validate `sid` or return an error if negative. <b>[ Previously rejected ]</b> </details> </details><!-- Macroscope's review summary ends here --> <!-- macroscope-ui-refresh --> <!-- Macroscope's pull request summary ends here -->
Add child resolution and ICE handling by extending
CursorStorewithiceandresolve_childrenacross in-memory, SQL, and wrapper implementationsExtend
CursorStorewithiceandresolve_children, add an in-memory icebox inInMemoryCursorStore, delegate persistence/lookup in the SQLite store, and switchGlobalCursortoBTreeMapwith merge viaapply. Update callers to construct and returnGlobalCursordirectly.📍Where to Start
Start with the trait changes in cursor_store.rs, then review the in-memory implementation in in_memory_cursor_store.rs and the SQLite store in cursor_store.rs.
📊 Macroscope summarized 0972aaf. 9 files reviewed, 6 issues evaluated, 6 issues filtered, 0 comments posted
🗂️ Filtered Issues
xmtp_db/src/encrypted_store/refresh_state.rs — 0 comments posted, 2 evaluated, 2 filtered
originator_id as u32andsequence_id.unwrap_or(0) as u64will wrap iforiginator_idorsequence_idare negative, producing large unsigned values instead of rejecting invalid input. This can corrupt the resultingGlobalCursor(e.g., mapping to the wrong originator or an enormous sequence), leading to incorrect cursor computations downstream. Validate non-negativity before casting, or return an error on invalid rows. [ Previously rejected ]entitiescontains no kinds that map togroup_messages(i.e., neitherEntityKind::ApplicationMessagenorEntityKind::CommitMessage). In that casegroup_message_kindsbecomes an empty Vec, producingkind IN ()in the constructed SQL, which is a syntax error in SQLite. This is reachable if callers pass, e.g., onlyEntityKind::Welcome. Ensure you guard against an emptygroup_message_kinds(e.g., omit thegroup_messagesbranch or use a false predicate) before constructing the placeholders. [ Out of scope ]xmtp_mls/src/cursor_store.rs — 0 comments posted, 4 evaluated, 4 filtered
sidfrom the database is cast tou64withaswithout validating non-negativity. Ifsidis negative (e.g., due to DB corruption or unexpected input), it will wrap to a very largeu64, corrupting the cursor and causing incorrect behavior (e.g., dominance/missing comparisons). Validatesid >= 0and return an error or clamp, or change the DB/API to return an unsigned type. [ Previously rejected ]sidis cast tou64without validating non-negativity inlatest_per_originatorfor identity updates, which can wrap negatives to huge values and corrupt cursor semantics. Validate or use an unsigned type. [ Previously rejected ]sidfromget_latest_sequence_id_for_inbox(...)is cast withas u64without validation. Ifsidis negative, the cast will wrap to a very largeu64, producing an incorrectGlobalCursorvalue and potentially causing downstream logic to skip messages. Validate thatsid >= 0and either clamp, normalize, or return an error before inserting into theGlobalCursor. Affected line:map.insert(Originators::INBOX_LOG, sid as u64);. [ Previously rejected ]sidis cast tou64without validation inlatest_for_topicsforIdentityUpdatesV1, which can wrap negative SIDs and produce incorrectGlobalCursorvalues. Validatesidor return an error if negative. [ Previously rejected ]