Newer
Older
System::on_finalize(b);
System::on_initialize(b + 1);
System::set_block_number(b + 1);
Shared::initializer_initialize(b + 1);
Paras::initializer_initialize(b + 1);
}
}
fn upgrade_at(expected_at: BlockNumber, activated_at: BlockNumber) -> ReplacementTimes<BlockNumber> {
ReplacementTimes { expected_at, activated_at }
}
fn check_code_is_stored(validation_code: &ValidationCode) {
assert!(<Paras as Store>::CodeByHashRefs::get(validation_code.hash()) != 0);
assert!(<Paras as Store>::CodeByHash::contains_key(validation_code.hash()));
}
fn check_code_is_not_stored(validation_code: &ValidationCode) {
assert!(!<Paras as Store>::CodeByHashRefs::contains_key(validation_code.hash()));
assert!(!<Paras as Store>::CodeByHash::contains_key(validation_code.hash()));
}
#[test]
fn para_past_code_meta_gives_right_code() {
let mut past_code = ParaPastCodeMeta::default();
assert_eq!(past_code.code_at(0u32), Some(UseCodeAt::Current));
past_code.note_replacement(10, 12);
assert_eq!(past_code.code_at(0), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(10), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(11), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(12), Some(UseCodeAt::Current));
past_code.note_replacement(20, 25);
assert_eq!(past_code.code_at(1), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(10), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(11), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(12), Some(UseCodeAt::ReplacedAt(20)));
assert_eq!(past_code.code_at(24), Some(UseCodeAt::ReplacedAt(20)));
assert_eq!(past_code.code_at(25), Some(UseCodeAt::Current));
past_code.note_replacement(30, 30);
assert_eq!(past_code.code_at(1), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(10), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(11), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(12), Some(UseCodeAt::ReplacedAt(20)));
assert_eq!(past_code.code_at(24), Some(UseCodeAt::ReplacedAt(20)));
assert_eq!(past_code.code_at(25), Some(UseCodeAt::ReplacedAt(30)));
assert_eq!(past_code.code_at(30), Some(UseCodeAt::Current));
past_code.last_pruned = Some(5);
assert_eq!(past_code.code_at(1), None);
assert_eq!(past_code.code_at(5), None);
assert_eq!(past_code.code_at(6), Some(UseCodeAt::ReplacedAt(10)));
assert_eq!(past_code.code_at(24), Some(UseCodeAt::ReplacedAt(20)));
assert_eq!(past_code.code_at(25), Some(UseCodeAt::ReplacedAt(30)));
assert_eq!(past_code.code_at(30), Some(UseCodeAt::Current));
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
}
#[test]
fn para_past_code_pruning_works_correctly() {
let mut past_code = ParaPastCodeMeta::default();
past_code.note_replacement(10u32, 10);
past_code.note_replacement(20, 25);
past_code.note_replacement(30, 35);
let old = past_code.clone();
assert!(past_code.prune_up_to(9).collect::<Vec<_>>().is_empty());
assert_eq!(old, past_code);
assert_eq!(past_code.prune_up_to(10).collect::<Vec<_>>(), vec![10]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![upgrade_at(20, 25), upgrade_at(30, 35)],
last_pruned: Some(10),
});
assert!(past_code.prune_up_to(21).collect::<Vec<_>>().is_empty());
assert_eq!(past_code.prune_up_to(26).collect::<Vec<_>>(), vec![20]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![upgrade_at(30, 35)],
last_pruned: Some(25),
});
past_code.note_replacement(40, 42);
past_code.note_replacement(50, 53);
past_code.note_replacement(60, 66);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![upgrade_at(30, 35), upgrade_at(40, 42), upgrade_at(50, 53), upgrade_at(60, 66)],
last_pruned: Some(25),
});
assert_eq!(past_code.prune_up_to(60).collect::<Vec<_>>(), vec![30, 40, 50]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![upgrade_at(60, 66)],
last_pruned: Some(53),
assert_eq!(past_code.most_recent_change(), Some(60));
assert_eq!(past_code.prune_up_to(66).collect::<Vec<_>>(), vec![60]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: Vec::new(),
last_pruned: Some(66),
});
}
#[test]
fn para_past_code_pruning_in_initialize() {
let code_retention_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: Default::default(),
}),
(1u32.into(), ParaGenesisArgs {
parachain: false,
genesis_head: Default::default(),
validation_code: Default::default(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let id = ParaId::from(0u32);
let at_block: BlockNumber = 10;
let included_block: BlockNumber = 12;
let validation_code = ValidationCode(vec![1, 2, 3]);
Paras::increase_code_ref(&validation_code.hash(), &validation_code);
<Paras as Store>::PastCodeHash::insert(&(id, at_block), &validation_code.hash());
<Paras as Store>::PastCodePruning::put(&vec![(id, included_block)]);
{
let mut code_meta = Paras::past_code_meta(&id);
code_meta.note_replacement(at_block, included_block);
<Paras as Store>::PastCodeMeta::insert(&id, &code_meta);
}
let pruned_at: BlockNumber = included_block + code_retention_period + 1;
assert_eq!(<Paras as Store>::PastCodeHash::get(&(id, at_block)), Some(validation_code.hash()));
check_code_is_stored(&validation_code);
run_to_block(pruned_at - 1, None);
assert_eq!(<Paras as Store>::PastCodeHash::get(&(id, at_block)), Some(validation_code.hash()));
assert_eq!(Paras::past_code_meta(&id).most_recent_change(), Some(at_block));
check_code_is_stored(&validation_code);
run_to_block(pruned_at, None);
assert!(<Paras as Store>::PastCodeHash::get(&(id, at_block)).is_none());
assert!(Paras::past_code_meta(&id).most_recent_change().is_none());
check_code_is_not_stored(&validation_code);
#[test]
fn note_new_head_sets_head() {
let code_retention_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: Default::default(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let id_a = ParaId::from(0u32);
assert_eq!(Paras::para_head(&id_a), Some(Default::default()));
Paras::note_new_head(id_a, vec![1, 2, 3].into(), 0);
assert_eq!(Paras::para_head(&id_a), Some(vec![1, 2, 3].into()));
});
}
#[test]
fn note_past_code_sets_up_pruning_correctly() {
let code_retention_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: Default::default(),
}),
(1u32.into(), ParaGenesisArgs {
parachain: false,
genesis_head: Default::default(),
validation_code: Default::default(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let id_a = ParaId::from(0u32);
let id_b = ParaId::from(1u32);
Paras::note_past_code(id_a, 10, 12, ValidationCode(vec![1, 2, 3]).hash());
Paras::note_past_code(id_b, 20, 23, ValidationCode(vec![4, 5, 6]).hash());
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
assert_eq!(<Paras as Store>::PastCodePruning::get(), vec![(id_a, 12), (id_b, 23)]);
assert_eq!(
Paras::past_code_meta(&id_a),
ParaPastCodeMeta {
upgrade_times: vec![upgrade_at(10, 12)],
last_pruned: None,
}
);
assert_eq!(
Paras::past_code_meta(&id_b),
ParaPastCodeMeta {
upgrade_times: vec![upgrade_at(20, 23)],
last_pruned: None,
}
);
});
}
#[test]
fn code_upgrade_applied_after_delay() {
let code_retention_period = 10;
let validation_upgrade_delay = 5;
let original_code = ValidationCode(vec![1, 2, 3]);
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: original_code.clone(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
validation_upgrade_delay,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
check_code_is_stored(&original_code);
let para_id = ParaId::from(0);
let new_code = ValidationCode(vec![4, 5, 6]);
run_to_block(2, None);
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
let expected_at = {
// this parablock is in the context of block 1.
let expected_at = 1 + validation_upgrade_delay;
Paras::schedule_code_upgrade(para_id, new_code.clone(), expected_at);
Paras::note_new_head(para_id, Default::default(), 1);
assert!(Paras::past_code_meta(¶_id).most_recent_change().is_none());
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(expected_at));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
check_code_is_stored(&original_code);
check_code_is_stored(&new_code);
expected_at
};
run_to_block(expected_at, None);
// the candidate is in the context of the parent of `expected_at`,
// thus does not trigger the code upgrade.
{
Paras::note_new_head(para_id, Default::default(), expected_at - 1);
assert!(Paras::past_code_meta(¶_id).most_recent_change().is_none());
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(expected_at));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
check_code_is_stored(&original_code);
check_code_is_stored(&new_code);
}
run_to_block(expected_at + 1, None);
// the candidate is in the context of `expected_at`, and triggers
// the upgrade.
{
Paras::note_new_head(para_id, Default::default(), expected_at);
assert_eq!(
Paras::past_code_meta(¶_id).most_recent_change(),
Some(expected_at),
);
assert_eq!(
<Paras as Store>::PastCodeHash::get(&(para_id, expected_at)),
Some(original_code.hash()),
);
assert!(<Paras as Store>::FutureCodeUpgrades::get(¶_id).is_none());
assert!(<Paras as Store>::FutureCodeHash::get(¶_id).is_none());
assert_eq!(Paras::current_code(¶_id), Some(new_code.clone()));
check_code_is_stored(&original_code);
check_code_is_stored(&new_code);
}
});
}
#[test]
fn code_upgrade_applied_after_delay_even_when_late() {
let code_retention_period = 10;
let validation_upgrade_delay = 5;
let original_code = ValidationCode(vec![1, 2, 3]);
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: original_code.clone(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
validation_upgrade_delay,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let para_id = ParaId::from(0);
let new_code = ValidationCode(vec![4, 5, 6]);
run_to_block(2, None);
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
let expected_at = {
// this parablock is in the context of block 1.
let expected_at = 1 + validation_upgrade_delay;
Paras::schedule_code_upgrade(para_id, new_code.clone(), expected_at);
Paras::note_new_head(para_id, Default::default(), 1);
assert!(Paras::past_code_meta(¶_id).most_recent_change().is_none());
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(expected_at));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
expected_at
};
run_to_block(expected_at + 1 + 4, None);
// the candidate is in the context of the first descendent of `expected_at`, and triggers
// the upgrade.
{
Paras::note_new_head(para_id, Default::default(), expected_at + 4);
assert_eq!(
Paras::past_code_meta(¶_id).most_recent_change(),
Some(expected_at),
);
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
// Some hypothetical block which would have triggered the code change
// should still use the old code.
assert_eq!(
Paras::past_code_meta(¶_id).code_at(expected_at),
Some(UseCodeAt::ReplacedAt(expected_at)),
);
// Some hypothetical block at the context which actually triggered the
// code change should still use the old code.
assert_eq!(
Paras::past_code_meta(¶_id).code_at(expected_at + 4),
Some(UseCodeAt::ReplacedAt(expected_at)),
);
// Some hypothetical block at the context after the code was upgraded
// should use the new code.
assert_eq!(
Paras::past_code_meta(¶_id).code_at(expected_at + 4 + 1),
Some(UseCodeAt::Current),
);
<Paras as Store>::PastCodeHash::get(&(para_id, expected_at)),
Some(original_code.hash()),
);
assert!(<Paras as Store>::FutureCodeUpgrades::get(¶_id).is_none());
assert!(<Paras as Store>::FutureCodeHash::get(¶_id).is_none());
assert_eq!(Paras::current_code(¶_id), Some(new_code.clone()));
}
});
}
#[test]
fn submit_code_change_when_not_allowed_is_err() {
let code_retention_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: vec![1, 2, 3].into(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let para_id = ParaId::from(0);
let new_code = ValidationCode(vec![4, 5, 6]);
let newer_code = ValidationCode(vec![4, 5, 6, 7]);
run_to_block(1, None);
Paras::schedule_code_upgrade(para_id, new_code.clone(), 8);
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(8));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
check_code_is_stored(&new_code);
Paras::schedule_code_upgrade(para_id, newer_code.clone(), 10);
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(8));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
check_code_is_not_stored(&newer_code);
});
}
#[test]
fn full_parachain_cleanup_storage() {
let code_retention_period = 10;
let original_code = ValidationCode(vec![1, 2, 3]);
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: original_code.clone(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
check_code_is_stored(&original_code);
let para_id = ParaId::from(0);
let new_code = ValidationCode(vec![4, 5, 6]);
run_to_block(2, None);
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
check_code_is_stored(&original_code);
let expected_at = {
// this parablock is in the context of block 1.
let expected_at = 1 + 5;
Paras::schedule_code_upgrade(para_id, new_code.clone(), expected_at);
Paras::note_new_head(para_id, Default::default(), 1);
assert!(Paras::past_code_meta(¶_id).most_recent_change().is_none());
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(expected_at));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
check_code_is_stored(&original_code);
check_code_is_stored(&new_code);
expected_at
};
assert_ok!(Paras::schedule_para_cleanup(para_id));
// Just scheduling cleanup shouldn't change anything.
{
assert_eq!(
<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()),
vec![para_id],
);
assert_eq!(Paras::parachains(), vec![para_id]);
assert!(Paras::past_code_meta(¶_id).most_recent_change().is_none());
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(expected_at));
assert_eq!(<Paras as Store>::FutureCodeHash::get(¶_id), Some(new_code.hash()));
assert_eq!(Paras::current_code(¶_id), Some(original_code.clone()));
check_code_is_stored(&original_code);
check_code_is_stored(&new_code);
assert_eq!(<Paras as Store>::Heads::get(¶_id), Some(Default::default()));
}
// run to block #4, with a 2 session changes at the end of the block 2 & 3.
run_to_block(4, Some(vec![3,4]));
// cleaning up the parachain should place the current parachain code
// into the past code buffer & schedule cleanup.
assert_eq!(Paras::past_code_meta(¶_id).most_recent_change(), Some(3));
assert_eq!(<Paras as Store>::PastCodeHash::get(&(para_id, 3)), Some(original_code.hash()));
assert_eq!(<Paras as Store>::PastCodePruning::get(), vec![(para_id, 3)]);
check_code_is_stored(&original_code);
// any future upgrades haven't been used to validate yet, so those
// are cleaned up immediately.
assert!(<Paras as Store>::FutureCodeUpgrades::get(¶_id).is_none());
assert!(<Paras as Store>::FutureCodeHash::get(¶_id).is_none());
assert!(Paras::current_code(¶_id).is_none());
check_code_is_not_stored(&new_code);
// run to do the final cleanup
let cleaned_up_at = 3 + code_retention_period + 1;
run_to_block(cleaned_up_at, None);
// now the final cleanup: last past code cleaned up, and this triggers meta cleanup.
assert_eq!(Paras::past_code_meta(¶_id), Default::default());
assert!(<Paras as Store>::PastCodeHash::get(&(para_id, 3)).is_none());
assert!(<Paras as Store>::PastCodePruning::get().is_empty());
check_code_is_not_stored(&original_code);
});
}
#[test]
fn para_incoming_at_session() {
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);
assert_ok!(Paras::schedule_para_initialize(
b,
ParaGenesisArgs {
parachain: true,
genesis_head: vec![1].into(),
validation_code: vec![1].into(),
},
assert_ok!(Paras::schedule_para_initialize(
a,
ParaGenesisArgs {
parachain: false,
genesis_head: vec![2].into(),
validation_code: vec![2].into(),
},
assert_ok!(Paras::schedule_para_initialize(
c,
ParaGenesisArgs {
parachain: true,
genesis_head: vec![3].into(),
validation_code: vec![3].into(),
},
assert_eq!(
<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()),
vec![c, b, a],
);
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::Onboarding));
// run to block without session change.
run_to_block(2, None);
assert_eq!(Paras::parachains(), Vec::new());
assert_eq!(
<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()),
vec![c, b, a],
);
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::Onboarding));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::Onboarding));
// Two sessions pass, so action queue is triggered
run_to_block(4, Some(vec![3,4]));
assert_eq!(Paras::parachains(), vec![c, b]);
assert_eq!(<Paras as Store>::ActionsQueue::get(Paras::scheduled_session()), Vec::new());
// Lifecycle is tracked correctly
assert_eq!(ParaLifecycles::get(&a), Some(ParaLifecycle::Parathread));
assert_eq!(ParaLifecycles::get(&b), Some(ParaLifecycle::Parachain));
assert_eq!(ParaLifecycles::get(&c), Some(ParaLifecycle::Parachain));
assert_eq!(Paras::current_code(&a), Some(vec![2].into()));
assert_eq!(Paras::current_code(&b), Some(vec![1].into()));
assert_eq!(Paras::current_code(&c), Some(vec![3].into()));
})
}
#[test]
fn code_at_with_intermediate() {
let code_retention_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: vec![1, 2, 3].into(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let para_id = ParaId::from(0);
let old_code: ValidationCode = vec![1, 2, 3].into();
let new_code: ValidationCode = vec![4, 5, 6].into();
Paras::schedule_code_upgrade(para_id, new_code.clone(), 10);
// no intermediate, falls back on current/past.
assert_eq!(Paras::validation_code_at(para_id, 1, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 10, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 100, None), Some(old_code.clone()));
// intermediate before upgrade meant to be applied, falls back on current.
assert_eq!(Paras::validation_code_at(para_id, 9, Some(8)), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 10, Some(9)), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 11, Some(9)), Some(old_code.clone()));
// intermediate at or after upgrade applied
assert_eq!(Paras::validation_code_at(para_id, 11, Some(10)), Some(new_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 100, Some(11)), Some(new_code.clone()));
run_to_block(code_retention_period + 5, None);
// at <= intermediate not allowed
assert_eq!(Paras::validation_code_at(para_id, 10, Some(10)), None);
assert_eq!(Paras::validation_code_at(para_id, 9, Some(10)), None);
});
}
#[test]
fn code_at_returns_up_to_end_of_code_retention_period() {
let code_retention_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: vec![1, 2, 3].into(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let para_id = ParaId::from(0);
let old_code: ValidationCode = vec![1, 2, 3].into();
let new_code: ValidationCode = vec![4, 5, 6].into();
Paras::schedule_code_upgrade(para_id, new_code.clone(), 2);
run_to_block(10, None);
Paras::note_new_head(para_id, Default::default(), 7);
assert_eq!(
Paras::past_code_meta(¶_id).upgrade_times,
vec![upgrade_at(2, 10)],
);
assert_eq!(Paras::validation_code_at(para_id, 2, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 3, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 9, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 10, None), Some(new_code.clone()));
run_to_block(10 + code_retention_period, None);
assert_eq!(Paras::validation_code_at(para_id, 2, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 3, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 9, None), Some(old_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 10, None), Some(new_code.clone()));
run_to_block(10 + code_retention_period + 1, None);
// code entry should be pruned now.
assert_eq!(
Paras::past_code_meta(¶_id),
ParaPastCodeMeta {
upgrade_times: Vec::new(),
last_pruned: Some(10),
},
);
assert_eq!(Paras::validation_code_at(para_id, 2, None), None); // pruned :(
assert_eq!(Paras::validation_code_at(para_id, 9, None), None);
assert_eq!(Paras::validation_code_at(para_id, 10, None), Some(new_code.clone()));
assert_eq!(Paras::validation_code_at(para_id, 11, None), Some(new_code.clone()));
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
#[test]
fn code_ref_is_cleaned_correctly() {
new_test_ext(Default::default()).execute_with(|| {
let code: ValidationCode = vec![1, 2, 3].into();
Paras::increase_code_ref(&code.hash(), &code);
Paras::increase_code_ref(&code.hash(), &code);
assert!(CodeByHash::contains_key(code.hash()));
assert_eq!(CodeByHashRefs::get(code.hash()), 2);
Paras::decrease_code_ref(&code.hash());
assert!(CodeByHash::contains_key(code.hash()));
assert_eq!(CodeByHashRefs::get(code.hash()), 1);
Paras::decrease_code_ref(&code.hash());
assert!(!CodeByHash::contains_key(code.hash()));
assert!(!CodeByHashRefs::contains_key(code.hash()));
});
}