Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions crates/xmtp_api_d14n/src/queries/combined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,22 +205,27 @@ where
.await
}

// Commit-log (fork detection) is deliberately not part of d14n's network
// ordering; it stays on a centralized v3-shaped service that is kept running
// through and past the cutover. So — unlike every other call — commit-log
// does NOT follow `choose_client`: a migrated client whose reads/writes go
// to xmtpd must keep publishing and reading its commit log on v3, or fork
// detection silently dies the moment it crosses the cutover (the d14n
// client's commit-log methods are deliberate no-ops). Route to v3
// unconditionally. No `write_with_refresh`: this service never emits the
// migration-rejection signal that reroutes writes to xmtpd.
async fn publish_commit_log(
&self,
request: mls_v1::BatchPublishCommitLogRequest,
) -> Result<(), Self::Error> {
self.write_with_refresh(|| {
let value = request.clone();
async move { self.choose_client().await?.publish_commit_log(value).await }
})
.await
self.v3_client.publish_commit_log(request).await
}

async fn query_commit_log(
&self,
request: mls_v1::BatchQueryCommitLogRequest,
) -> Result<mls_v1::BatchQueryCommitLogResponse, Self::Error> {
self.choose_client().await?.query_commit_log(request).await
self.v3_client.query_commit_log(request).await
}

async fn get_newest_group_message(
Expand Down
39 changes: 39 additions & 0 deletions crates/xmtp_api_d14n/src/queries/combined/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,42 @@ async fn is_d14n_returns_true_after_migration() {

assert!(client.is_d14n()?);
}

/// Commit-log (fork detection) must stay on the retained centralized v3 service
/// even after the client has migrated to d14n. With `has_migrated = true` every
/// other call routes to the xmtpd client, but `publish_commit_log` /
/// `query_commit_log` must still hit v3 — the d14n mock has no expectations, so
/// a call wrongly routed there panics.
#[xmtp_common::test(unwrap_try = true)]
async fn commit_log_stays_on_v3_after_migration() {
use xmtp_proto::mls_v1::{BatchPublishCommitLogRequest, BatchQueryCommitLogRequest};

let store = InMemoryCursorStore::new();
store.set_has_migrated(true)?;

// v3 mock counts every request and answers with an empty (default-decoding)
// body; d14n mock is left bare so any commit-log call routed to it panics.
let v3_calls = Arc::new(std::sync::atomic::AtomicU32::new(0));
let v3 = {
let calls = v3_calls.clone();
let mut mock = MockNetworkClient::new();
mock.expect_request().returning(move |_req, _path, _body| {
calls.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
Ok(http::Response::new(prost::bytes::Bytes::new()))
});
TestNetworkClient::from_mock(mock)
};
let d14n = TestNetworkClient::new();

let client = build_test_client(v3, d14n, store);

client
.publish_commit_log(BatchPublishCommitLogRequest::default())
.await?;
client
.query_commit_log(BatchQueryCommitLogRequest::default())
.await?;

// Both calls reached v3, never the migrated (d14n) client.
assert_eq!(v3_calls.load(std::sync::atomic::Ordering::SeqCst), 2);
}
Loading