diff --git a/polkadot/runtime/parachains/src/paras.rs b/polkadot/runtime/parachains/src/paras.rs index 15e69bde6d..1f65bcd0c2 100644 --- a/polkadot/runtime/parachains/src/paras.rs +++ b/polkadot/runtime/parachains/src/paras.rs @@ -411,7 +411,19 @@ impl Module { /// Schedule a para to be cleaned up at the start of the next session. pub fn schedule_para_cleanup(id: ParaId) -> Weight { - OutgoingParas::mutate(|v| { + let upcoming_weight = UpcomingParas::mutate(|v| { + match v.binary_search(&id) { + Ok(i) => { + v.remove(i); + UpcomingParasGenesis::remove(id); + // If a para was only in the pending state it should not be moved to `Outgoing` + return T::DbWeight::get().reads_writes(2, 2); + } + Err(_) => T::DbWeight::get().reads_writes(1, 0), + } + }); + + let outgoing_weight = OutgoingParas::mutate(|v| { match v.binary_search(&id) { Ok(_) => T::DbWeight::get().reads_writes(1, 0), Err(i) => { @@ -419,7 +431,9 @@ impl Module { T::DbWeight::get().reads_writes(1, 1) } } - }) + }); + + outgoing_weight + upcoming_weight } /// Schedule a future code upgrade of the given parachain, to be applied after inclusion @@ -1149,6 +1163,70 @@ mod tests { }) } + #[test] + fn para_cleanup_removes_upcoming() { + new_test_ext(Default::default()).execute_with(|| { + run_to_block(1, None); + + let b = ParaId::from(525); + let a = ParaId::from(999); + let c = ParaId::from(333); + + Paras::schedule_para_initialize( + b, + ParaGenesisArgs { + parachain: true, + genesis_head: vec![1].into(), + validation_code: vec![1].into(), + }, + ); + + Paras::schedule_para_initialize( + a, + ParaGenesisArgs { + parachain: false, + genesis_head: vec![2].into(), + validation_code: vec![2].into(), + }, + ); + + Paras::schedule_para_initialize( + c, + ParaGenesisArgs { + parachain: true, + genesis_head: vec![3].into(), + validation_code: vec![3].into(), + }, + ); + + assert_eq!(::UpcomingParas::get(), vec![c, b, a]); + assert!(::Parathreads::get(&a).is_none()); + + + // run to block without session change. + run_to_block(2, None); + + assert_eq!(Paras::parachains(), Vec::new()); + assert_eq!(::UpcomingParas::get(), vec![c, b, a]); + assert!(::Parathreads::get(&a).is_none()); + + Paras::schedule_para_cleanup(c); + + run_to_block(3, Some(vec![3])); + + assert_eq!(Paras::parachains(), vec![b]); + assert_eq!(::OutgoingParas::get(), vec![]); + assert_eq!(::UpcomingParas::get(), Vec::new()); + assert!(::UpcomingParasGenesis::get(a).is_none()); + + assert!(::Parathreads::get(&a).is_some()); + + assert_eq!(Paras::current_code(&a), Some(vec![2].into())); + assert_eq!(Paras::current_code(&b), Some(vec![1].into())); + assert!(Paras::current_code(&c).is_none()); + }); + } + #[test] fn code_at_with_intermediate() { let acceptance_period = 10;