Skip to content
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

multiple create error when adding address on dev #1746

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
112 changes: 107 additions & 5 deletions bindings_ffi/src/mls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2656,6 +2656,7 @@ impl FfiGroupPermissions {

#[cfg(test)]
mod tests {
use futures::future::{join_all, try_join_all};
use passkey::{
authenticator::{Authenticator, UserCheck, UserValidationMethod},
client::{Client, DefaultClientData},
Expand Down Expand Up @@ -2703,6 +2704,7 @@ mod tests {
FfiMessageWithReactions, FfiMetadataField, FfiMultiRemoteAttachment, FfiPasskeySignature,
FfiPermissionPolicy, FfiPermissionPolicySet, FfiPermissionUpdateType, FfiReaction,
FfiReactionAction, FfiReactionSchema, FfiRemoteAttachmentInfo, FfiSubscribeError,
GenericError,
};
use ethers::utils::hex;
use prost::Message;
Expand Down Expand Up @@ -2907,31 +2909,57 @@ mod tests {
client.register_identity(signature_request).await.unwrap();
}

async fn register_clientt(
inbox_owner: &LocalWalletInboxOwner,
client: &FfiXmtpClient,
) -> Result<(), GenericError> {
let signature_request = client.signature_request().unwrap();
signature_request
.add_ecdsa_signature(
inbox_owner
.sign(signature_request.signature_text().await.unwrap())
.unwrap(),
)
.await?;
client.register_identity(signature_request).await?;

Ok(())
}

/// Create a new test client with a given wallet.
async fn new_test_client_with_wallet(
wallet: xmtp_cryptography::utils::LocalWallet,
) -> Arc<FfiXmtpClient> {
new_test_client_with_wallet_and_history_sync_url(wallet, None).await
new_test_client_with_wallet_and_history_sync_url(wallet, None, false).await
}

/// Create a new test client with a given wallet.
async fn new_test_client_with_wallet_dev(
wallet: xmtp_cryptography::utils::LocalWallet,
) -> Arc<FfiXmtpClient> {
new_test_client_with_wallet_and_history_sync_url(wallet, None, true).await
}

async fn new_test_client_with_wallet_and_history(
wallet: xmtp_cryptography::utils::LocalWallet,
) -> Arc<FfiXmtpClient> {
new_test_client_with_wallet_and_history_sync_url(wallet, Some(HISTORY_SYNC_URL.to_string()))
new_test_client_with_wallet_and_history_sync_url(wallet, Some(HISTORY_SYNC_URL.to_string()), false)
.await
}

async fn new_test_client_with_wallet_and_history_sync_url(
wallet: xmtp_cryptography::utils::LocalWallet,
history_sync_url: Option<String>,
is_dev: bool,
) -> Arc<FfiXmtpClient> {
let ffi_inbox_owner = LocalWalletInboxOwner::with_wallet(wallet);
let ident = ffi_inbox_owner.identifier();
let nonce = 1;
let inbox_id = ident.inbox_id(nonce).unwrap();

let backend_address = if is_dev { xmtp_api_grpc::DEV_ADDRESS.to_string() } else { xmtp_api_grpc::LOCALHOST_ADDRESS.to_string() };
let client = create_client(
connect_to_backend(xmtp_api_grpc::LOCALHOST_ADDRESS.to_string(), false)
connect_to_backend(backend_address, false)
.await
.unwrap(),
Some(tmp_path()),
Expand All @@ -2952,14 +2980,48 @@ mod tests {
client
}

async fn new_test_clientt(
wallet: xmtp_cryptography::utils::LocalWallet,
) -> Result<Arc<FfiXmtpClient>, GenericError> {
let ffi_inbox_owner = LocalWalletInboxOwner::with_wallet(wallet);
let ident = ffi_inbox_owner.identifier();
let nonce = 1;
let inbox_id = ident.inbox_id(nonce).unwrap();

let client = create_client(
connect_to_backend(xmtp_api_grpc::DEV_ADDRESS.to_string(), false)
.await
.unwrap(),
Some(tmp_path()),
Some(xmtp_mls::storage::EncryptedMessageStore::generate_enc_key().into()),
&inbox_id,
ident,
nonce,
None,
None,
)
.await?;

let conn = client.inner_client.context().store().conn().unwrap();
conn.register_triggers();

register_clientt(&ffi_inbox_owner, &client).await?;

Ok(client)
}

async fn new_test_client() -> Arc<FfiXmtpClient> {
let wallet = xmtp_cryptography::utils::LocalWallet::new(&mut rng());
new_test_client_with_wallet(wallet).await
}


async fn new_test_client_dev() -> Arc<FfiXmtpClient> {
let wallet = xmtp_cryptography::utils::LocalWallet::new(&mut rng());
new_test_client_with_wallet_dev(wallet).await
}
async fn new_test_client_with_history() -> Arc<FfiXmtpClient> {
let wallet = xmtp_cryptography::utils::LocalWallet::new(&mut rng());
new_test_client_with_wallet_and_history_sync_url(wallet, Some(HISTORY_SYNC_URL.to_string()))
new_test_client_with_wallet_and_history_sync_url(wallet, Some(HISTORY_SYNC_URL.to_string()), false)
.await
}

Expand Down Expand Up @@ -3213,6 +3275,27 @@ mod tests {
assert_eq!(updated_state.members().len(), 3);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
async fn rapidfire_duplicate_create() {
let wallet = generate_local_wallet();
let mut futs = vec![];
for _ in 0..10 {
futs.push(new_test_clientt(wallet.clone()));
}

let results = join_all(futs).await;

let mut num_okay = 0;
for result in results {
if result.is_ok() {
num_okay += 1;
}
}

// Only one client should get to sign up
assert_eq!(num_okay, 1);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn associate_passkey() {
let alex = new_test_client().await;
Expand Down Expand Up @@ -7429,4 +7512,23 @@ mod tests {
assert_eq!(decoded.url, original.url);
}
}

#[tokio::test]
async fn test_broken_address_on_dev() {
let client = new_test_client_dev().await;
let broken_identifier = FfiIdentifier {
identifier: "0x72984f2c4136e081583b062d88da7166c0da2bf2".to_string(),
identifier_kind: FfiIdentifierKind::Ethereum,
};
let api = connect_to_backend(xmtp_api_grpc::DEV_ADDRESS.to_string(), false)
.await
.unwrap();


// let group = client.conversations().create_group(vec![broken_identifier], FfiCreateGroupOptions::default()).await.unwrap();
let inbox_id = get_inbox_id_for_identifier(api, broken_identifier).await.unwrap().unwrap();
assert_eq!(inbox_id, "7881166c8d3f128a5a3d56c0596134544710f742981e0eaf650bb4facab80971");
// Errors on next line with `Err Client(Group(InstallationDiff(Client(Association(MultipleCreate)))))`
let _group = client.conversations().create_group_with_inbox_ids(vec!["7881166c8d3f128a5a3d56c0596134544710f742981e0eaf650bb4facab80971".to_string()], FfiCreateGroupOptions::default()).await.unwrap();
}
}
50 changes: 50 additions & 0 deletions xmtp_id/src/associations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,4 +727,54 @@ pub(crate) mod tests {
));
}
}

#[wasm_bindgen_test(unsupported = test)]
fn test_duplicate_create_inbox() {
// Create first inbox
let create_request = CreateInbox::default();
let inbox_id = create_request
.account_identifier
.inbox_id(create_request.nonce)
.unwrap();

let identity_update =
IdentityUpdate::new_test(vec![Action::CreateInbox(create_request.clone())], inbox_id.clone());

// Create a duplicate create inbox action with the same account and nonce
let duplicate_update =
IdentityUpdate::new_test(vec![Action::CreateInbox(create_request)], inbox_id.clone());

// First update should succeed
let initial_state = get_state(vec![identity_update]).unwrap();
assert_eq!(initial_state.members().len(), 1);

// Applying the duplicate update should fail with MultipleCreate error
let result = apply_update(initial_state, duplicate_update);
assert!(result.is_err());
assert!(matches!(result, Err(AssociationError::MultipleCreate)));
}

#[wasm_bindgen_test(unsupported = test)]
fn test_duplicate_create_inbox_in_batch() {
// Create a create inbox action
let create_request = CreateInbox::default();
let inbox_id = create_request
.account_identifier
.inbox_id(create_request.nonce)
.unwrap();

// Create an identity update with two identical create inbox actions
let identity_update = IdentityUpdate::new_test(
vec![
Action::CreateInbox(create_request.clone()),
Action::CreateInbox(create_request),
],
inbox_id.clone(),
);

// Applying the update with duplicate create actions should fail
let result = get_state(vec![identity_update]);
assert!(result.is_err());
assert!(matches!(result, Err(AssociationError::MultipleCreate)));
}
}
Loading