From cf4e77da4b709e69899e404951113fb652faf48c Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Thu, 19 Sep 2024 15:07:29 -0300 Subject: [PATCH 1/6] Set invalid proposals; Check proposal expiration date --- pallets/multisig/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/pallets/multisig/src/lib.rs b/pallets/multisig/src/lib.rs index 4d3941992c..2ebfb62f70 100644 --- a/pallets/multisig/src/lib.rs +++ b/pallets/multisig/src/lib.rs @@ -677,6 +677,10 @@ pub mod pallet { TooManySigners, /// Multisig doesn't have a paying DID. NoPayingDid, + /// Expiry must be in the future. + InvalidExpiryDate, + /// The proposal has been invalidated after a multisg update. + InvalidatedProposal, } /// Nonce to ensure unique MultiSig addresses are generated; starts from 1. @@ -783,6 +787,14 @@ pub mod pallet { #[pallet::getter(fn transaction_version)] pub(super) type TransactionVersion = StorageValue<_, u32, ValueQuery>; + /// The last proposal id before the multisig changed signers or signatures required. + /// + /// multisig => Option + #[pallet::storage] + #[pallet::getter(fn last_invalid_proposal)] + pub type LastInvalidProposal = + StorageMap<_, Identity, T::AccountId, u64, OptionQuery>; + /// Storage version. #[pallet::storage] #[pallet::getter(fn storage_version)] @@ -876,8 +888,12 @@ impl Pallet { Some(ProposalState::ExecutionSuccessful | ProposalState::ExecutionFailed) => { Err(Error::::ProposalAlreadyExecuted.into()) } - Some(ProposalState::Active { until: None }) => Ok(()), + Some(ProposalState::Active { until: None }) => { + Self::ensure_valid_proposal(multisig, proposal_id)?; + Ok(()) + } Some(ProposalState::Active { until: Some(until) }) => { + Self::ensure_valid_proposal(multisig, proposal_id)?; // Ensure proposal is not expired ensure!( until > pallet_timestamp::Pallet::::get(), @@ -915,6 +931,7 @@ impl Pallet { Self::ensure_max_signers(&multisig, signers.len() as u64)?; Self::base_authorize_signers(ms_did, &multisig, &signers)?; + Self::set_invalid_proposals(&multisig); Self::deposit_event(Event::MultiSigSignersAuthorized { caller_did: caller_did.unwrap_or(ms_did), multisig, @@ -949,6 +966,7 @@ impl Pallet { } NumberOfSigners::::insert(&multisig, pending_num_of_signers); + Self::set_invalid_proposals(&multisig); Self::deposit_event(Event::MultiSigSignersRemoved { caller_did: caller_did.unwrap_or(ms_did), multisig: multisig.clone(), @@ -1004,6 +1022,7 @@ impl Pallet { let max_weight = proposal.get_dispatch_info().weight; let caller_did = Self::ensure_ms_get_did(multisig)?; let proposal_id = Self::next_proposal_id(multisig); + Self::ensure_valid_expiry(&expiry)?; Proposals::::insert(multisig, proposal_id, &*proposal); ProposalVoteCounts::::insert(multisig, proposal_id, ProposalVoteCount::default()); @@ -1280,6 +1299,7 @@ impl Pallet { Error::::ChangeNotAllowed ); MultiSigSignsRequired::::insert(multisig, &signatures_required); + Self::set_invalid_proposals(&multisig); Self::deposit_event(Event::MultiSigSignersRequiredChanged { caller_did: caller_did.or(ms_did), multisig: multisig.clone(), @@ -1287,6 +1307,40 @@ impl Pallet { }); Ok(()) } + + /// Returns `Ok` if `expiry` is in the future. Otherwise, returns [`Error::InvalidExpiryDate`]. + fn ensure_valid_expiry(expiry: &Option) -> DispatchResult { + if let Some(expiry) = expiry { + ensure!( + expiry > &pallet_timestamp::Pallet::::get(), + Error::::InvalidExpiryDate + ); + } + Ok(()) + } + + /// Returns `Ok` if `proposal_id` is valid. Otherwise, returns [`InvalidatedProposal`]. + fn ensure_valid_proposal(multisig: &T::AccountId, proposal_id: u64) -> DispatchResult { + if let Some(last_invalid_proposal) = Self::last_invalid_proposal(multisig) { + ensure!( + proposal_id > last_invalid_proposal, + Error::::InvalidatedProposal + ); + } + Ok(()) + } + + /// Sets [`LastInvalidProposal`] with the proposal id of the last proposal. + fn set_invalid_proposals(multisig: &T::AccountId) { + let next_proposal_id = Self::next_proposal_id(multisig); + + // There are no proposals for the multisig + if next_proposal_id == 0 { + return; + } + + LastInvalidProposal::::insert(multisig, next_proposal_id.saturating_sub(1)); + } } impl MultiSigSubTrait for Pallet { From 34a9d5ef8765ce1700dcbc01b667cd06000bd9f1 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Fri, 20 Sep 2024 11:17:31 -0300 Subject: [PATCH 2/6] Add unit tests --- pallets/multisig/src/lib.rs | 2 +- pallets/runtime/tests/src/multisig.rs | 259 +++++++++++++++++++++++++- 2 files changed, 259 insertions(+), 2 deletions(-) diff --git a/pallets/multisig/src/lib.rs b/pallets/multisig/src/lib.rs index 2ebfb62f70..bd67ac5934 100644 --- a/pallets/multisig/src/lib.rs +++ b/pallets/multisig/src/lib.rs @@ -1319,7 +1319,7 @@ impl Pallet { Ok(()) } - /// Returns `Ok` if `proposal_id` is valid. Otherwise, returns [`InvalidatedProposal`]. + /// Returns `Ok` if `proposal_id` is valid. Otherwise, returns [`Error::InvalidatedProposal`]. fn ensure_valid_proposal(multisig: &T::AccountId, proposal_id: u64) -> DispatchResult { if let Some(last_invalid_proposal) = Self::last_invalid_proposal(multisig) { ensure!( diff --git a/pallets/runtime/tests/src/multisig.rs b/pallets/runtime/tests/src/multisig.rs index 19bdb7fa1a..db151c1f87 100644 --- a/pallets/runtime/tests/src/multisig.rs +++ b/pallets/runtime/tests/src/multisig.rs @@ -3,7 +3,9 @@ use frame_support::{ dispatch::DispatchResult, BoundedVec, }; -use pallet_multisig::{self as multisig, AdminDid, ProposalStates, ProposalVoteCounts, Votes}; +use pallet_multisig::{ + self as multisig, AdminDid, LastInvalidProposal, ProposalStates, ProposalVoteCounts, Votes, +}; use polymesh_common_utilities::constants::currency::POLY; use polymesh_primitives::multisig::ProposalState; use polymesh_primitives::{AccountId, AuthorizationData, Permissions, SecondaryKey, Signatory}; @@ -1395,6 +1397,261 @@ fn multisig_nesting_not_allowed() { }); } +#[test] +fn create_expired_proposal() { + ExtBuilder::default().build().execute_with(|| { + let alice = User::new(AccountKeyring::Alice); + let bob_key = AccountKeyring::Bob.to_account_id(); + let ferdie_key = AccountKeyring::Ferdie.to_account_id(); + let ferdie = Origin::signed(ferdie_key.clone()); + let charlie_key = AccountKeyring::Charlie.to_account_id(); + + let ms_address = setup_multisig( + alice.acc(), + 3, + create_signers(vec![ferdie_key, bob_key, charlie_key]), + ); + + let expires_at = 100u64; + let call = Box::new(RuntimeCall::MultiSig( + multisig::Call::change_sigs_required { sigs_required: 2 }, + )); + + set_timestamp(expires_at); + + assert_eq!( + MultiSig::create_proposal(ferdie.clone(), ms_address.clone(), call, Some(100u64),) + .unwrap_err() + .error, + Error::InvalidExpiryDate.into() + ) + }); +} + +#[test] +fn invalidate_proposals_change_sigs_required() { + ExtBuilder::default().build().execute_with(|| { + let alice = User::new(AccountKeyring::Alice); + let bob_key = AccountKeyring::Bob.to_account_id(); + let ferdie_key = AccountKeyring::Ferdie.to_account_id(); + let ferdie = Origin::signed(ferdie_key.clone()); + let charlie_key = AccountKeyring::Charlie.to_account_id(); + + let ms_address = setup_multisig( + alice.acc(), + 3, + create_signers(vec![ferdie_key, bob_key, charlie_key]), + ); + + let call = Box::new(RuntimeCall::MultiSig( + multisig::Call::change_sigs_required { sigs_required: 2 }, + )); + + assert_ok!(MultiSig::create_proposal( + ferdie.clone(), + ms_address.clone(), + call, + None + )); + + assert_ok!(MultiSig::change_sigs_required_via_admin( + alice.origin(), + ms_address.clone(), + 2 + )); + + assert_eq!( + LastInvalidProposal::::get(&ms_address), + Some(0) + ); + + assert_eq!( + MultiSig::approve(ferdie.clone(), ms_address.clone(), 0, None) + .unwrap_err() + .error, + Error::InvalidatedProposal.into() + ); + assert_eq!( + MultiSig::reject(ferdie, ms_address, 0).unwrap_err().error, + Error::InvalidatedProposal.into() + ); + }); +} + +#[test] +fn invalidate_proposals_add_signer() { + ExtBuilder::default().build().execute_with(|| { + let alice = User::new(AccountKeyring::Alice); + let bob_key = AccountKeyring::Bob.to_account_id(); + let ferdie_key = AccountKeyring::Ferdie.to_account_id(); + let ferdie = Origin::signed(ferdie_key.clone()); + let charlie_key = AccountKeyring::Charlie.to_account_id(); + + let ms_address = setup_multisig(alice.acc(), 2, create_signers(vec![ferdie_key, bob_key])); + + let call = Box::new(RuntimeCall::MultiSig( + multisig::Call::change_sigs_required { sigs_required: 2 }, + )); + + assert_ok!(MultiSig::create_proposal( + ferdie.clone(), + ms_address.clone(), + call, + None + )); + + assert_ok!(MultiSig::add_multisig_signers_via_admin( + alice.origin(), + ms_address.clone(), + create_signers(vec![charlie_key]) + )); + + assert_eq!( + LastInvalidProposal::::get(&ms_address), + Some(0) + ); + + assert_eq!( + MultiSig::approve(ferdie.clone(), ms_address.clone(), 0, None) + .unwrap_err() + .error, + Error::InvalidatedProposal.into() + ); + assert_eq!( + MultiSig::reject(ferdie, ms_address, 0).unwrap_err().error, + Error::InvalidatedProposal.into() + ); + }); +} + +#[test] +fn invalidate_proposals_remove_signer() { + ExtBuilder::default().build().execute_with(|| { + let alice = User::new(AccountKeyring::Alice); + let bob_key = AccountKeyring::Bob.to_account_id(); + let ferdie_key = AccountKeyring::Ferdie.to_account_id(); + let ferdie = Origin::signed(ferdie_key.clone()); + let charlie_key = AccountKeyring::Charlie.to_account_id(); + + let ms_address = setup_multisig( + alice.acc(), + 2, + create_signers(vec![ferdie_key, bob_key, charlie_key.clone()]), + ); + + let call = Box::new(RuntimeCall::MultiSig( + multisig::Call::change_sigs_required { sigs_required: 2 }, + )); + + assert_ok!(MultiSig::create_proposal( + ferdie.clone(), + ms_address.clone(), + call, + None + )); + + assert_ok!(MultiSig::remove_multisig_signers_via_admin( + alice.origin(), + ms_address.clone(), + create_signers(vec![charlie_key]) + )); + + assert_eq!( + LastInvalidProposal::::get(&ms_address), + Some(0) + ); + + assert_eq!( + MultiSig::approve(ferdie.clone(), ms_address.clone(), 0, None) + .unwrap_err() + .error, + Error::InvalidatedProposal.into() + ); + assert_eq!( + MultiSig::reject(ferdie, ms_address, 0).unwrap_err().error, + Error::InvalidatedProposal.into() + ); + }); +} + +#[test] +fn invalidate_proposals_via_executed_proposal() { + ExtBuilder::default().build().execute_with(|| { + let alice = User::new(AccountKeyring::Alice); + let bob = Origin::signed(AccountKeyring::Bob.to_account_id()); + let bob_signer = AccountKeyring::Bob.to_account_id(); + let charlie = Origin::signed(AccountKeyring::Charlie.to_account_id()); + let charlie_signer = AccountKeyring::Charlie.to_account_id(); + + let ms_address = create_multisig_default_perms( + alice.acc(), + create_signers(vec![charlie_signer.clone(), bob_signer.clone()]), + 2, + ); + + let charlie_auth_id = get_last_auth_id(&charlie_signer); + assert_ok!(MultiSig::accept_multisig_signer( + charlie.clone(), + charlie_auth_id + )); + + let bob_auth_id = get_last_auth_id(&bob_signer); + assert_ok!(MultiSig::accept_multisig_signer(bob.clone(), bob_auth_id)); + + let call = Box::new(RuntimeCall::MultiSig( + multisig::Call::change_sigs_required { sigs_required: 1 }, + )); + assert_ok!(MultiSig::create_proposal( + bob.clone(), + ms_address.clone(), + call.clone(), + None, + )); + + assert_ok!(MultiSig::create_proposal( + bob.clone(), + ms_address.clone(), + call.clone(), + None, + )); + + assert_ok!(MultiSig::create_proposal( + bob.clone(), + ms_address.clone(), + call, + None, + )); + + assert_ok!(MultiSig::approve( + charlie.clone(), + ms_address.clone(), + 0, + None + )); + + // At this point the proposal of id 0 executes + next_block(); + + // All other proposals must have been invalidated + assert_eq!( + LastInvalidProposal::::get(&ms_address), + Some(2) + ); + assert_eq!( + MultiSig::approve(charlie.clone(), ms_address.clone(), 1, None) + .unwrap_err() + .error, + Error::InvalidatedProposal.into() + ); + assert_eq!( + MultiSig::reject(charlie.clone(), ms_address, 2) + .unwrap_err() + .error, + Error::InvalidatedProposal.into() + ); + }); +} + fn setup_multisig( creator: AccountId, sigs_required: u64, From c39d04f7151f0c61e119561ad7692d4235f07308 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Fri, 20 Sep 2024 14:42:59 -0300 Subject: [PATCH 3/6] Add remove_admin extrinsic --- pallets/common/src/traits/multisig.rs | 1 + pallets/multisig/src/benchmarking.rs | 5 +++ pallets/multisig/src/lib.rs | 19 ++++++++++-- pallets/runtime/tests/src/multisig.rs | 42 ++++++++++++++++++++++++++ pallets/weights/src/pallet_multisig.rs | 14 +++++++++ 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/pallets/common/src/traits/multisig.rs b/pallets/common/src/traits/multisig.rs index 69bc4de65d..eff0801190 100644 --- a/pallets/common/src/traits/multisig.rs +++ b/pallets/common/src/traits/multisig.rs @@ -40,6 +40,7 @@ pub trait WeightInfo { fn create_join_identity() -> Weight; fn approve_join_identity() -> Weight; fn join_identity() -> Weight; + fn remove_admin() -> Weight; fn default_max_weight(max_weight: &Option) -> Weight { max_weight.unwrap_or_else(|| { diff --git a/pallets/multisig/src/benchmarking.rs b/pallets/multisig/src/benchmarking.rs index f03e94d77c..8150fa7168 100644 --- a/pallets/multisig/src/benchmarking.rs +++ b/pallets/multisig/src/benchmarking.rs @@ -369,4 +369,9 @@ benchmarks! { MultiSig::::approve_join_identity(users[0].origin().into(), multisig.clone(), auth_id).unwrap(); // The second approval call just approves. }: approve_join_identity(users[1].origin(), multisig, auth_id) + + remove_admin { + let (alice, multisig, _, _, multisig_origin) = generate_multisig_for_alice::(2, 2).unwrap(); + init_admin(&multisig, &alice); + }: _(multisig_origin) } diff --git a/pallets/multisig/src/lib.rs b/pallets/multisig/src/lib.rs index bd67ac5934..4a489db95e 100644 --- a/pallets/multisig/src/lib.rs +++ b/pallets/multisig/src/lib.rs @@ -451,7 +451,7 @@ pub mod pallet { Self::deposit_event(Event::MultiSigRemovedAdmin { caller_did: admin_did, multisig, - admin_did, + admin_did: Some(admin_did), }); Ok(().into()) } @@ -533,6 +533,21 @@ pub mod pallet { pallet_identity::Module::::join_identity(origin, auth_id)?; Ok(().into()) } + + /// Removes the admin identity from the `multisig`. This must be called by the multisig itself. + #[pallet::call_index(17)] + #[pallet::weight(::WeightInfo::remove_admin())] + pub fn remove_admin(origin: OriginFor) -> DispatchResultWithPostInfo { + let multisig = ensure_signed(origin)?; + let caller_did = Self::ensure_ms_has_did(&multisig)?; + let admin_did = AdminDid::::take(&multisig); + Self::deposit_event(Event::MultiSigRemovedAdmin { + caller_did, + multisig, + admin_did, + }); + Ok(().into()) + } } #[pallet::event] @@ -619,7 +634,7 @@ pub mod pallet { MultiSigRemovedAdmin { caller_did: IdentityId, multisig: T::AccountId, - admin_did: IdentityId, + admin_did: Option, }, /// A Multisig has removed it's paying DID. MultiSigRemovedPayingDid { diff --git a/pallets/runtime/tests/src/multisig.rs b/pallets/runtime/tests/src/multisig.rs index db151c1f87..4b87a997ba 100644 --- a/pallets/runtime/tests/src/multisig.rs +++ b/pallets/runtime/tests/src/multisig.rs @@ -1255,6 +1255,48 @@ fn proposal_owner_rejection_denied() { }); } +#[test] +fn remove_admin_successfully() { + ExtBuilder::default().build().execute_with(|| { + // Multisig creator + let alice = User::new(AccountKeyring::Alice); + // Multisig signers + let ferdie_signer = AccountKeyring::Ferdie.to_account_id(); + let bob_signer = AccountKeyring::Bob.to_account_id(); + let charlie_signer = AccountKeyring::Charlie.to_account_id(); + + let ms_address = create_multisig_default_perms( + alice.acc(), + create_signers(vec![ferdie_signer, bob_signer.clone(), charlie_signer]), + 2, + ); + assert!(AdminDid::::get(&ms_address).is_some()); + + assert_ok!(MultiSig::remove_admin(Origin::signed(ms_address.clone()))); + assert!(AdminDid::::get(ms_address).is_none()) + }); +} + +#[test] +fn remove_admin_not_multsig() { + ExtBuilder::default().build().execute_with(|| { + // Multisig creator + let alice = User::new(AccountKeyring::Alice); + // Multisig signers + let ferdie_signer = AccountKeyring::Ferdie.to_account_id(); + let bob_signer = AccountKeyring::Bob.to_account_id(); + let charlie_signer = AccountKeyring::Charlie.to_account_id(); + + create_multisig_default_perms( + alice.acc(), + create_signers(vec![ferdie_signer, bob_signer.clone(), charlie_signer]), + 2, + ); + + assert_noop!(MultiSig::remove_admin(alice.origin()), Error::NoSuchMultisig); + }); +} + fn expired_proposals() { ExtBuilder::default().build().execute_with(|| { let alice = User::new(AccountKeyring::Alice); diff --git a/pallets/weights/src/pallet_multisig.rs b/pallets/weights/src/pallet_multisig.rs index 6e8cdd8e2e..32d45bda29 100644 --- a/pallets/weights/src/pallet_multisig.rs +++ b/pallets/weights/src/pallet_multisig.rs @@ -471,4 +471,18 @@ impl pallet_multisig::WeightInfo for SubstrateWeight { .saturating_add(DbWeight::get().reads(8)) .saturating_add(DbWeight::get().writes(2)) } + // Storage: MultiSig MultiSigSignsRequired (r:1 w:0) + // Proof Skipped: MultiSig MultiSigSignsRequired (max_values: None, max_size: None, mode: Measured) + // Storage: Identity KeyRecords (r:1 w:0) + // Proof Skipped: Identity KeyRecords (max_values: None, max_size: None, mode: Measured) + // Storage: Identity IsDidFrozen (r:1 w:0) + // Proof Skipped: Identity IsDidFrozen (max_values: None, max_size: None, mode: Measured) + // Storage: MultiSig AdminDid (r:1 w:1) + // Proof Skipped: MultiSig AdminDid (max_values: None, max_size: None, mode: Measured) + fn remove_admin() -> Weight { + // Minimum execution time: 29_555 nanoseconds. + Weight::from_ref_time(29_694_000) + .saturating_add(DbWeight::get().reads(4)) + .saturating_add(DbWeight::get().writes(1)) + } } From 0a8da07a945c6f623080860530497b6c99987141 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Fri, 20 Sep 2024 14:43:24 -0300 Subject: [PATCH 4/6] Cargo fmt --- pallets/runtime/tests/src/multisig.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pallets/runtime/tests/src/multisig.rs b/pallets/runtime/tests/src/multisig.rs index 4b87a997ba..54ebfe7343 100644 --- a/pallets/runtime/tests/src/multisig.rs +++ b/pallets/runtime/tests/src/multisig.rs @@ -1293,7 +1293,10 @@ fn remove_admin_not_multsig() { 2, ); - assert_noop!(MultiSig::remove_admin(alice.origin()), Error::NoSuchMultisig); + assert_noop!( + MultiSig::remove_admin(alice.origin()), + Error::NoSuchMultisig + ); }); } From 6ea5c4812e231efda22c2458f287a45deaccb55a Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Mon, 30 Sep 2024 15:06:31 -0300 Subject: [PATCH 5/6] Return error if mutlsig has no admin; Invalidate proposal only when accepting multisig signer --- pallets/multisig/src/lib.rs | 11 +++++++---- pallets/runtime/tests/src/multisig.rs | 11 ++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/pallets/multisig/src/lib.rs b/pallets/multisig/src/lib.rs index 4a489db95e..ac1a338b59 100644 --- a/pallets/multisig/src/lib.rs +++ b/pallets/multisig/src/lib.rs @@ -451,7 +451,7 @@ pub mod pallet { Self::deposit_event(Event::MultiSigRemovedAdmin { caller_did: admin_did, multisig, - admin_did: Some(admin_did), + admin_did, }); Ok(().into()) } @@ -540,7 +540,7 @@ pub mod pallet { pub fn remove_admin(origin: OriginFor) -> DispatchResultWithPostInfo { let multisig = ensure_signed(origin)?; let caller_did = Self::ensure_ms_has_did(&multisig)?; - let admin_did = AdminDid::::take(&multisig); + let admin_did = AdminDid::::take(&multisig).ok_or(Error::::AdminNotFound)?; Self::deposit_event(Event::MultiSigRemovedAdmin { caller_did, multisig, @@ -634,7 +634,7 @@ pub mod pallet { MultiSigRemovedAdmin { caller_did: IdentityId, multisig: T::AccountId, - admin_did: Option, + admin_did: IdentityId, }, /// A Multisig has removed it's paying DID. MultiSigRemovedPayingDid { @@ -696,6 +696,8 @@ pub mod pallet { InvalidExpiryDate, /// The proposal has been invalidated after a multisg update. InvalidatedProposal, + /// Multisig has not admin. + AdminNotFound, } /// Nonce to ensure unique MultiSig addresses are generated; starts from 1. @@ -946,7 +948,6 @@ impl Pallet { Self::ensure_max_signers(&multisig, signers.len() as u64)?; Self::base_authorize_signers(ms_did, &multisig, &signers)?; - Self::set_invalid_proposals(&multisig); Self::deposit_event(Event::MultiSigSignersAuthorized { caller_did: caller_did.unwrap_or(ms_did), multisig, @@ -1245,6 +1246,8 @@ impl Pallet { IdentityPallet::::ensure_auth_by(ms_identity, auth_by)?; + Self::set_invalid_proposals(&multisig); + // Update number of signers for this multisig. let pending_num_of_signers = Self::ensure_max_signers(&multisig, 1)?; NumberOfSigners::::insert(&multisig, pending_num_of_signers); diff --git a/pallets/runtime/tests/src/multisig.rs b/pallets/runtime/tests/src/multisig.rs index 54ebfe7343..a9bd77af66 100644 --- a/pallets/runtime/tests/src/multisig.rs +++ b/pallets/runtime/tests/src/multisig.rs @@ -1530,6 +1530,7 @@ fn invalidate_proposals_add_signer() { let bob_key = AccountKeyring::Bob.to_account_id(); let ferdie_key = AccountKeyring::Ferdie.to_account_id(); let ferdie = Origin::signed(ferdie_key.clone()); + let charlie = Origin::signed(AccountKeyring::Charlie.to_account_id()); let charlie_key = AccountKeyring::Charlie.to_account_id(); let ms_address = setup_multisig(alice.acc(), 2, create_signers(vec![ferdie_key, bob_key])); @@ -1548,7 +1549,15 @@ fn invalidate_proposals_add_signer() { assert_ok!(MultiSig::add_multisig_signers_via_admin( alice.origin(), ms_address.clone(), - create_signers(vec![charlie_key]) + create_signers(vec![charlie_key.clone()]) + )); + + assert_eq!(LastInvalidProposal::::get(&ms_address), None); + + let charlie_auth_id = get_last_auth_id(&charlie_key); + assert_ok!(MultiSig::accept_multisig_signer( + charlie.clone(), + charlie_auth_id )); assert_eq!( From a474b0d7a591c878e49f70e71fb731dc2d64e2d7 Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Tue, 1 Oct 2024 10:42:42 -0300 Subject: [PATCH 6/6] Fix typo --- pallets/multisig/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/multisig/src/lib.rs b/pallets/multisig/src/lib.rs index ac1a338b59..4cfef8116f 100644 --- a/pallets/multisig/src/lib.rs +++ b/pallets/multisig/src/lib.rs @@ -696,7 +696,7 @@ pub mod pallet { InvalidExpiryDate, /// The proposal has been invalidated after a multisg update. InvalidatedProposal, - /// Multisig has not admin. + /// Multisig has no admin. AdminNotFound, }