diff --git a/pallets/runtime/tests/src/settlement_test.rs b/pallets/runtime/tests/src/settlement_test.rs index 3675beb75a..3a0aecfdf0 100644 --- a/pallets/runtime/tests/src/settlement_test.rs +++ b/pallets/runtime/tests/src/settlement_test.rs @@ -2317,6 +2317,59 @@ fn assert_number_of_venue_signers() { }) } +#[test] +fn duplicate_venue_signers_rejected() { + ExtBuilder::default().build().execute_with(|| { + let alice = User::new(Sr25519Keyring::Alice); + let venue_id = VenueCounter::::get(); + let bob = Sr25519Keyring::Bob.to_account_id(); + let charlie = Sr25519Keyring::Charlie.to_account_id(); + + // Duplicates in create_venue rejected. + assert_noop!( + Settlement::create_venue( + alice.origin(), + VenueDetails::default(), + vec![bob.clone(), bob.clone()], + VenueType::Exchange + ), + Error::DuplicateSigners + ); + + // Create venue with unique signers. + assert_ok!(Settlement::create_venue( + alice.origin(), + VenueDetails::default(), + vec![bob.clone(), charlie.clone()], + VenueType::Exchange + )); + assert_eq!(NumberOfVenueSigners::::get(venue_id), 2); + + // Duplicates in remove rejected — counter stays in sync. + assert_noop!( + Settlement::update_venue_signers( + alice.origin(), + venue_id, + vec![bob.clone(), bob.clone()], + false + ), + Error::DuplicateSigners + ); + assert_eq!(NumberOfVenueSigners::::get(venue_id), 2); + + // Duplicates in add rejected. + assert_noop!( + Settlement::update_venue_signers( + alice.origin(), + venue_id, + vec![Sr25519Keyring::Dave.to_account_id(); 2], + true + ), + Error::DuplicateSigners + ); + }); +} + #[test] fn reject_instruction_with_zero_amount() { test_with_did_registrar(|_eve| { diff --git a/pallets/settlement/src/lib.rs b/pallets/settlement/src/lib.rs index 13b2840d68..724f3f7b93 100644 --- a/pallets/settlement/src/lib.rs +++ b/pallets/settlement/src/lib.rs @@ -585,6 +585,8 @@ pub mod pallet { InvalidTaskName, /// The receipt has expired and can no longer be claimed. ReceiptExpired, + /// The signers list contains duplicate entries. + DuplicateSigners, } const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); @@ -809,6 +811,10 @@ pub mod pallet { let did = pallet_identity::Pallet::::ensure_perms(origin)?; ensure_string_limited::(&details)?; + let mut seen = BTreeSet::new(); + for signer in &signers { + ensure!(seen.insert(signer), Error::::DuplicateSigners); + } ensure!( signers.len() <= T::MaxNumberOfVenueSigners::get() as usize, Error::::NumberOfVenueSignersExceeded @@ -2562,6 +2568,11 @@ impl Pallet { // Ensure venue exists & sender is its creator. Self::ensure_venue_creator(&venue_id, &did)?; + let mut seen = BTreeSet::new(); + for signer in &signers { + ensure!(seen.insert(signer), Error::::DuplicateSigners); + } + if add_signers { let current_number_of_signers = NumberOfVenueSigners::::get(venue_id); ensure!(