Introduce a maximum code size and head data size (#835)

* add a maximum code size and head data size

* get existing tests passing

* add tests for slots logic

* test registrar behavior

* introduce maximums and bump versions

* address review grumbles

* work around publicizing derive

* remove unneeded and wrong doc
This commit is contained in:
Robert Habermeier
2020-02-11 19:21:56 +01:00
committed by GitHub
parent 9b23f3f1f0
commit 295151338d
6 changed files with 314 additions and 19 deletions
+55 -6
View File
@@ -121,6 +121,15 @@ pub enum LastContribution<BlockNumber> {
#[derive(Encode, Decode, Clone, PartialEq, Eq)] #[derive(Encode, Decode, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))] #[cfg_attr(feature = "std", derive(Debug))]
struct DeployData<Hash> {
code_hash: Hash,
code_size: u32,
initial_head_data: Vec<u8>,
}
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
#[codec(dumb_trait_bound)]
pub struct FundInfo<AccountId, Balance, Hash, BlockNumber> { pub struct FundInfo<AccountId, Balance, Hash, BlockNumber> {
/// The parachain that this fund has funded, if there is one. As long as this is `Some`, then /// The parachain that this fund has funded, if there is one. As long as this is `Some`, then
/// the funds may not be withdrawn and the fund cannot be dissolved. /// the funds may not be withdrawn and the fund cannot be dissolved.
@@ -150,8 +159,8 @@ pub struct FundInfo<AccountId, Balance, Hash, BlockNumber> {
/// BlockNumber. /// BlockNumber.
last_slot: BlockNumber, last_slot: BlockNumber,
/// The deployment data associated with this fund, if any. Once set it may not be reset. First /// The deployment data associated with this fund, if any. Once set it may not be reset. First
/// is the code hash, second is the initial head data. /// is the code hash, second is the code size, third is the initial head data.
deploy_data: Option<(Hash, Vec<u8>)>, deploy_data: Option<DeployData<Hash>>,
} }
decl_storage! { decl_storage! {
@@ -346,6 +355,7 @@ decl_module! {
fn fix_deploy_data(origin, fn fix_deploy_data(origin,
#[compact] index: FundIndex, #[compact] index: FundIndex,
code_hash: T::Hash, code_hash: T::Hash,
code_size: u32,
initial_head_data: Vec<u8> initial_head_data: Vec<u8>
) { ) {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
@@ -354,7 +364,7 @@ decl_module! {
ensure!(fund.owner == who, Error::<T>::InvalidOrigin); // must be fund owner ensure!(fund.owner == who, Error::<T>::InvalidOrigin); // must be fund owner
ensure!(fund.deploy_data.is_none(), Error::<T>::ExistingDeployData); ensure!(fund.deploy_data.is_none(), Error::<T>::ExistingDeployData);
fund.deploy_data = Some((code_hash, initial_head_data)); fund.deploy_data = Some(DeployData { code_hash, code_size, initial_head_data });
<Funds<T>>::insert(index, &fund); <Funds<T>>::insert(index, &fund);
@@ -374,12 +384,20 @@ decl_module! {
let _ = ensure_signed(origin)?; let _ = ensure_signed(origin)?;
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?; let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
let (code_hash, initial_head_data) = fund.clone().deploy_data.ok_or(Error::<T>::UnsetDeployData)?; let DeployData { code_hash, code_size, initial_head_data }
= fund.clone().deploy_data.ok_or(Error::<T>::UnsetDeployData)?;
ensure!(fund.parachain.is_none(), Error::<T>::AlreadyOnboard); ensure!(fund.parachain.is_none(), Error::<T>::AlreadyOnboard);
fund.parachain = Some(para_id); fund.parachain = Some(para_id);
let fund_origin = system::RawOrigin::Signed(Self::fund_account_id(index)).into(); let fund_origin = system::RawOrigin::Signed(Self::fund_account_id(index)).into();
<slots::Module<T>>::fix_deploy_data(fund_origin, index, para_id, code_hash, initial_head_data)?; <slots::Module<T>>::fix_deploy_data(
fund_origin,
index,
para_id,
code_hash,
code_size,
initial_head_data,
)?;
<Funds<T>>::insert(index, &fund); <Funds<T>>::insert(index, &fund);
@@ -650,6 +668,9 @@ mod tests {
RefCell<HashMap<u32, (Vec<u8>, Vec<u8>)>> = RefCell::new(HashMap::new()); RefCell<HashMap<u32, (Vec<u8>, Vec<u8>)>> = RefCell::new(HashMap::new());
} }
const MAX_CODE_SIZE: u32 = 100;
const MAX_HEAD_DATA_SIZE: u32 = 10;
pub struct TestParachains; pub struct TestParachains;
impl Registrar<u64> for TestParachains { impl Registrar<u64> for TestParachains {
fn new_id() -> ParaId { fn new_id() -> ParaId {
@@ -659,6 +680,14 @@ mod tests {
}) })
} }
fn head_data_size_allowed(head_data_size: u32) -> bool {
head_data_size <= MAX_HEAD_DATA_SIZE
}
fn code_size_allowed(code_size: u32) -> bool {
code_size <= MAX_CODE_SIZE
}
fn register_para( fn register_para(
id: ParaId, id: ParaId,
_info: ParaInfo, _info: ParaInfo,
@@ -875,13 +904,21 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
let fund = Crowdfund::funds(0).unwrap(); let fund = Crowdfund::funds(0).unwrap();
// Confirm deploy data is stored correctly // Confirm deploy data is stored correctly
assert_eq!(fund.deploy_data, Some((<Test as system::Trait>::Hash::default(), vec![0]))); assert_eq!(
fund.deploy_data,
Some(DeployData {
code_hash: <Test as system::Trait>::Hash::default(),
code_size: 0,
initial_head_data: vec![0],
}),
);
}); });
} }
@@ -897,6 +934,7 @@ mod tests {
Origin::signed(2), Origin::signed(2),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0]), vec![0]),
Error::<Test>::InvalidOrigin Error::<Test>::InvalidOrigin
); );
@@ -906,6 +944,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
1, 1,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0]), vec![0]),
Error::<Test>::InvalidFundIndex Error::<Test>::InvalidFundIndex
); );
@@ -915,6 +954,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
@@ -922,6 +962,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![1]), vec![1]),
Error::<Test>::ExistingDeployData Error::<Test>::ExistingDeployData
); );
@@ -941,6 +982,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
@@ -986,6 +1028,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
@@ -1013,6 +1056,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
@@ -1055,6 +1099,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
@@ -1196,6 +1241,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
assert_ok!(Crowdfund::onboard(Origin::signed(1), 0, 0.into())); assert_ok!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()));
@@ -1224,6 +1270,7 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
// Move to the end of auction... // Move to the end of auction...
@@ -1262,12 +1309,14 @@ mod tests {
Origin::signed(1), Origin::signed(1),
0, 0,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
assert_ok!(Crowdfund::fix_deploy_data( assert_ok!(Crowdfund::fix_deploy_data(
Origin::signed(2), Origin::signed(2),
1, 1,
<Test as system::Trait>::Hash::default(), <Test as system::Trait>::Hash::default(),
0,
vec![0] vec![0]
)); ));
+24
View File
@@ -129,6 +129,14 @@ pub trait Trait: attestations::Trait {
/// The way that we are able to register parachains. /// The way that we are able to register parachains.
type Registrar: Registrar<Self::AccountId>; type Registrar: Registrar<Self::AccountId>;
/// Maximum code size for parachains, in bytes. Note that this is not
/// the entire storage burden of the parachain, as old code is stored for
/// `SlashPeriod` blocks.
type MaxCodeSize: Get<u32>;
/// Max head data size.
type MaxHeadDataSize: Get<u32>;
} }
/// Origin for the parachains module. /// Origin for the parachains module.
@@ -233,6 +241,8 @@ decl_error! {
UntaggedVotes, UntaggedVotes,
/// Wrong parent head for parachain receipt. /// Wrong parent head for parachain receipt.
ParentMismatch, ParentMismatch,
/// Head data was too large.
HeadDataTooLarge,
} }
} }
@@ -778,6 +788,8 @@ impl<T: Trait> Module<T> {
let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]); let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]);
let mut para_block_hashes = Vec::new(); let mut para_block_hashes = Vec::new();
let max_head_data_size = T::MaxHeadDataSize::get();
for candidate in attested_candidates { for candidate in attested_candidates {
let para_id = candidate.parachain_index(); let para_id = candidate.parachain_index();
let validator_group = validator_groups.group_for(para_id) let validator_group = validator_groups.group_for(para_id)
@@ -801,6 +813,11 @@ impl<T: Trait> Module<T> {
Error::<T>::VotesExceedsAuthorities, Error::<T>::VotesExceedsAuthorities,
); );
ensure!(
max_head_data_size >= candidate.candidate().head_data.0.len() as _,
Error::<T>::HeadDataTooLarge,
);
let fees = candidate.candidate().fees; let fees = candidate.candidate().fees;
T::ParachainCurrency::deduct(para_id, fees)?; T::ParachainCurrency::deduct(para_id, fees)?;
@@ -1152,6 +1169,11 @@ mod tests {
type MaxRetries = MaxRetries; type MaxRetries = MaxRetries;
} }
parameter_types! {
pub const MaxHeadDataSize: u32 = 100;
pub const MaxCodeSize: u32 = 100;
}
impl Trait for Test { impl Trait for Test {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
@@ -1159,6 +1181,8 @@ mod tests {
type Randomness = RandomnessCollectiveFlip; type Randomness = RandomnessCollectiveFlip;
type ActiveParachains = registrar::Module<Test>; type ActiveParachains = registrar::Module<Test>;
type Registrar = registrar::Module<Test>; type Registrar = registrar::Module<Test>;
type MaxCodeSize = MaxCodeSize;
type MaxHeadDataSize = MaxHeadDataSize;
} }
type Parachains = Module<Test>; type Parachains = Module<Test>;
+87 -2
View File
@@ -46,8 +46,19 @@ pub trait Registrar<AccountId> {
/// Create a new unique parachain identity for later registration. /// Create a new unique parachain identity for later registration.
fn new_id() -> ParaId; fn new_id() -> ParaId;
/// Checks whether the given initial head data size falls within the limit.
fn head_data_size_allowed(head_data_size: u32) -> bool;
/// Checks whether the given validation code falls within the limit.
fn code_size_allowed(code_size: u32) -> bool;
/// Register a parachain with given `code` and `initial_head_data`. `id` must not yet be registered or it will /// Register a parachain with given `code` and `initial_head_data`. `id` must not yet be registered or it will
/// result in a error. /// result in a error.
///
/// This does not enforce any code size or initial head data limits, as these
/// are governable and parameters for parachain initialization are often
/// determined long ahead-of-time. Not checking these values ensures that changes to limits
/// do not invalidate in-progress auction winners.
fn register_para( fn register_para(
id: ParaId, id: ParaId,
info: ParaInfo, info: ParaInfo,
@@ -64,6 +75,14 @@ impl<T: Trait> Registrar<T::AccountId> for Module<T> {
<NextFreeId>::mutate(|n| { let r = *n; *n = ParaId::from(u32::from(*n) + 1); r }) <NextFreeId>::mutate(|n| { let r = *n; *n = ParaId::from(u32::from(*n) + 1); r })
} }
fn head_data_size_allowed(head_data_size: u32) -> bool {
head_data_size <= <T as parachains::Trait>::MaxHeadDataSize::get()
}
fn code_size_allowed(code_size: u32) -> bool {
code_size <= <T as parachains::Trait>::MaxCodeSize::get()
}
fn register_para( fn register_para(
id: ParaId, id: ParaId,
info: ParaInfo, info: ParaInfo,
@@ -71,6 +90,7 @@ impl<T: Trait> Registrar<T::AccountId> for Module<T> {
initial_head_data: Vec<u8>, initial_head_data: Vec<u8>,
) -> DispatchResult { ) -> DispatchResult {
ensure!(!Paras::exists(id), Error::<T>::ParaAlreadyExists); ensure!(!Paras::exists(id), Error::<T>::ParaAlreadyExists);
if let Scheduling::Always = info.scheduling { if let Scheduling::Always = info.scheduling {
Parachains::mutate(|parachains| Parachains::mutate(|parachains|
match parachains.binary_search(&id) { match parachains.binary_search(&id) {
@@ -222,6 +242,10 @@ decl_error! {
InvalidChainId, InvalidChainId,
/// Invalid parathread ID. /// Invalid parathread ID.
InvalidThreadId, InvalidThreadId,
/// Invalid para code size.
CodeTooLarge,
/// Invalid para head data size.
HeadDataTooLarge,
} }
} }
@@ -232,8 +256,11 @@ decl_module! {
fn deposit_event() = default; fn deposit_event() = default;
/// Register a parachain with given code. /// Register a parachain with given code. Must be called by root.
/// Fails if given ID is already used. /// Fails if given ID is already used.
///
/// Unlike the `Registrar` trait function of the same name, this
/// checks the code and head data against size limits.
#[weight = SimpleDispatchInfo::FixedOperational(5_000_000)] #[weight = SimpleDispatchInfo::FixedOperational(5_000_000)]
pub fn register_para(origin, pub fn register_para(origin,
#[compact] id: ParaId, #[compact] id: ParaId,
@@ -242,6 +269,18 @@ decl_module! {
initial_head_data: Vec<u8>, initial_head_data: Vec<u8>,
) -> DispatchResult { ) -> DispatchResult {
ensure_root(origin)?; ensure_root(origin)?;
ensure!(
<Self as Registrar<T::AccountId>>::code_size_allowed(code.len() as _),
Error::<T>::CodeTooLarge,
);
ensure!(
<Self as Registrar<T::AccountId>>::head_data_size_allowed(
initial_head_data.len() as _
),
Error::<T>::HeadDataTooLarge,
);
<Self as Registrar<T::AccountId>>:: <Self as Registrar<T::AccountId>>::
register_para(id, info, code, initial_head_data) register_para(id, info, code, initial_head_data)
} }
@@ -267,6 +306,10 @@ decl_module! {
/// ///
/// Must be sent from a Signed origin that is able to have ParathreadDeposit reserved. /// Must be sent from a Signed origin that is able to have ParathreadDeposit reserved.
/// `code` and `initial_head_data` are used to initialize the parathread's state. /// `code` and `initial_head_data` are used to initialize the parathread's state.
///
/// Unlike `register_para`, this function does check that the maximum code size
/// and head data size are respected, as parathread registration is an atomic
/// action.
fn register_parathread(origin, fn register_parathread(origin,
code: Vec<u8>, code: Vec<u8>,
initial_head_data: Vec<u8>, initial_head_data: Vec<u8>,
@@ -278,6 +321,19 @@ decl_module! {
let info = ParaInfo { let info = ParaInfo {
scheduling: Scheduling::Dynamic, scheduling: Scheduling::Dynamic,
}; };
ensure!(
<Self as Registrar<T::AccountId>>::code_size_allowed(code.len() as _),
Error::<T>::CodeTooLarge,
);
ensure!(
<Self as Registrar<T::AccountId>>::head_data_size_allowed(
initial_head_data.len() as _
),
Error::<T>::HeadDataTooLarge,
);
let id = <Self as Registrar<T::AccountId>>::new_id(); let id = <Self as Registrar<T::AccountId>>::new_id();
let _ = <Self as Registrar<T::AccountId>>:: let _ = <Self as Registrar<T::AccountId>>::
@@ -709,6 +765,11 @@ mod tests {
type DisabledValidatorsThreshold = DisabledValidatorsThreshold; type DisabledValidatorsThreshold = DisabledValidatorsThreshold;
} }
parameter_types! {
pub const MaxHeadDataSize: u32 = 100;
pub const MaxCodeSize: u32 = 100;
}
impl parachains::Trait for Test { impl parachains::Trait for Test {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
@@ -716,6 +777,8 @@ mod tests {
type ActiveParachains = Registrar; type ActiveParachains = Registrar;
type Registrar = Registrar; type Registrar = Registrar;
type Randomness = RandomnessCollectiveFlip; type Randomness = RandomnessCollectiveFlip;
type MaxCodeSize = MaxCodeSize;
type MaxHeadDataSize = MaxHeadDataSize;
} }
parameter_types! { parameter_types! {
@@ -929,7 +992,7 @@ mod tests {
run_to_block(10); run_to_block(10);
let h = BlakeTwo256::hash(&[2u8; 3]); let h = BlakeTwo256::hash(&[2u8; 3]);
assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, user_id(1), h, vec![2; 3])); assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, user_id(1), h, 3, vec![2; 3]));
assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), user_id(1), vec![2; 3])); assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), user_id(1), vec![2; 3]));
assert_ok!(Slots::set_offboarding(Origin::signed(user_id(1).into_account()), 1)); assert_ok!(Slots::set_offboarding(Origin::signed(user_id(1).into_account()), 1));
@@ -1380,4 +1443,26 @@ mod tests {
); );
}); });
} }
#[test]
fn register_does_not_enforce_limits_when_registering() {
new_test_ext(vec![]).execute_with(|| {
let bad_code_size = <Test as parachains::Trait>::MaxCodeSize::get() + 1;
let bad_head_size = <Test as parachains::Trait>::MaxHeadDataSize::get() + 1;
let code = vec![1u8; bad_code_size as _];
let head_data = vec![2u8; bad_head_size as _];
assert!(!<Registrar as super::Registrar<u64>>::code_size_allowed(bad_code_size));
assert!(!<Registrar as super::Registrar<u64>>::head_data_size_allowed(bad_head_size));
let id = <Registrar as super::Registrar<u64>>::new_id();
assert_ok!(<Registrar as super::Registrar<u64>>::register_para(
id,
ParaInfo { scheduling: Scheduling::Always },
code,
head_data,
));
});
}
} }
+132 -9
View File
@@ -108,8 +108,12 @@ impl<AccountId: Clone + Default + Codec> Bidder<AccountId> {
pub enum IncomingParachain<AccountId, Hash> { pub enum IncomingParachain<AccountId, Hash> {
/// Deploy information not yet set; just the bidder identity. /// Deploy information not yet set; just the bidder identity.
Unset(NewBidder<AccountId>), Unset(NewBidder<AccountId>),
/// Deploy information set only by code hash; so we store the code hash and head data. /// Deploy information set only by code hash; so we store the code hash, code size, and head data.
Fixed { code_hash: Hash, initial_head_data: Vec<u8> }, ///
/// The code size must be included so that checks against a maximum code size
/// can be done. If the size of the preimage of the code hash does not match
/// the given code size, it will not be possible to register the parachain.
Fixed { code_hash: Hash, code_size: u32, initial_head_data: Vec<u8> },
/// Deploy information fully set; so we store the code and head data. /// Deploy information fully set; so we store the code and head data.
Deploy { code: Vec<u8>, initial_head_data: Vec<u8> }, Deploy { code: Vec<u8>, initial_head_data: Vec<u8> },
} }
@@ -250,6 +254,10 @@ decl_error! {
NotCurrentAuction, NotCurrentAuction,
/// Not an auction. /// Not an auction.
NotAuction, NotAuction,
/// Given code size is too large.
CodeTooLarge,
/// Given initial head data is too large.
HeadDataTooLarge,
} }
} }
@@ -402,6 +410,7 @@ decl_module! {
#[compact] sub: SubId, #[compact] sub: SubId,
#[compact] para_id: ParaId, #[compact] para_id: ParaId,
code_hash: T::Hash, code_hash: T::Hash,
code_size: u32,
initial_head_data: Vec<u8> initial_head_data: Vec<u8>
) { ) {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
@@ -412,7 +421,17 @@ decl_module! {
} else { } else {
Err(Error::<T>::AlreadyRegistered)? Err(Error::<T>::AlreadyRegistered)?
} }
let item = (starts, IncomingParachain::Fixed{code_hash, initial_head_data});
ensure!(
T::Parachains::head_data_size_allowed(initial_head_data.len() as _),
Error::<T>::HeadDataTooLarge,
);
ensure!(
T::Parachains::code_size_allowed(code_size),
Error::<T>::CodeTooLarge,
);
let item = (starts, IncomingParachain::Fixed{code_hash, code_size, initial_head_data});
<Onboarding<T>>::insert(&para_id, item); <Onboarding<T>>::insert(&para_id, item);
} }
@@ -432,8 +451,10 @@ decl_module! {
pub fn elaborate_deploy_data(_origin, #[compact] para_id: ParaId, code: Vec<u8>) -> DispatchResult { pub fn elaborate_deploy_data(_origin, #[compact] para_id: ParaId, code: Vec<u8>) -> DispatchResult {
let (starts, details) = <Onboarding<T>>::get(&para_id) let (starts, details) = <Onboarding<T>>::get(&para_id)
.ok_or(Error::<T>::ParaNotOnboarding)?; .ok_or(Error::<T>::ParaNotOnboarding)?;
if let IncomingParachain::Fixed{code_hash, initial_head_data} = details { if let IncomingParachain::Fixed{code_hash, code_size, initial_head_data} = details {
ensure!(code.len() as u32 == code_size, Error::<T>::InvalidCode);
ensure!(<T as system::Trait>::Hashing::hash(&code) == code_hash, Error::<T>::InvalidCode); ensure!(<T as system::Trait>::Hashing::hash(&code) == code_hash, Error::<T>::InvalidCode);
if starts > Self::lease_period_index() { if starts > Self::lease_period_index() {
// Hasn't yet begun. Replace the on-boarding entry with the new information. // Hasn't yet begun. Replace the on-boarding entry with the new information.
let item = (starts, IncomingParachain::Deploy{code, initial_head_data}); let item = (starts, IncomingParachain::Deploy{code, initial_head_data});
@@ -918,6 +939,9 @@ mod tests {
RefCell<HashMap<u32, (Vec<u8>, Vec<u8>)>> = RefCell::new(HashMap::new()); RefCell<HashMap<u32, (Vec<u8>, Vec<u8>)>> = RefCell::new(HashMap::new());
} }
const MAX_CODE_SIZE: u32 = 100;
const MAX_HEAD_DATA_SIZE: u32 = 10;
pub struct TestParachains; pub struct TestParachains;
impl Registrar<u64> for TestParachains { impl Registrar<u64> for TestParachains {
fn new_id() -> ParaId { fn new_id() -> ParaId {
@@ -926,6 +950,15 @@ mod tests {
(*p.borrow() - 1).into() (*p.borrow() - 1).into()
}) })
} }
fn head_data_size_allowed(head_data_size: u32) -> bool {
head_data_size <= MAX_HEAD_DATA_SIZE
}
fn code_size_allowed(code_size: u32) -> bool {
code_size <= MAX_CODE_SIZE
}
fn register_para( fn register_para(
id: ParaId, id: ParaId,
_info: ParaInfo, _info: ParaInfo,
@@ -1144,7 +1177,7 @@ mod tests {
run_to_block(9); run_to_block(9);
let h = BlakeTwo256::hash(&[42u8][..]); let h = BlakeTwo256::hash(&[42u8][..]);
assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, vec![69])); assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, 1, vec![69]));
assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![42])); assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![42]));
run_to_block(10); run_to_block(10);
@@ -1169,7 +1202,7 @@ mod tests {
run_to_block(11); run_to_block(11);
let h = BlakeTwo256::hash(&[42u8][..]); let h = BlakeTwo256::hash(&[42u8][..]);
assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, vec![69])); assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, 1, vec![69]));
assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![42])); assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![42]));
with_parachains(|p| { with_parachains(|p| {
assert_eq!(p.len(), 1); assert_eq!(p.len(), 1);
@@ -1279,7 +1312,7 @@ mod tests {
for &(para, sub, acc) in &[(0, 0, 1), (1, 0, 2), (2, 0, 3), (3, 1, 4), (4, 1, 5)] { for &(para, sub, acc) in &[(0, 0, 1), (1, 0, 2), (2, 0, 3), (3, 1, 4), (4, 1, 5)] {
let h = BlakeTwo256::hash(&[acc][..]); let h = BlakeTwo256::hash(&[acc][..]);
assert_ok!(Slots::fix_deploy_data(Origin::signed(acc as _), sub, para.into(), h, vec![acc])); assert_ok!(Slots::fix_deploy_data(Origin::signed(acc as _), sub, para.into(), h, 1, vec![acc]));
assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), para.into(), vec![acc])); assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), para.into(), vec![acc]));
} }
@@ -1326,7 +1359,7 @@ mod tests {
run_to_block(10); run_to_block(10);
let h = BlakeTwo256::hash(&[1u8][..]); let h = BlakeTwo256::hash(&[1u8][..]);
assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, vec![1])); assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, 1, vec![1]));
assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![1])); assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![1]));
assert_ok!(Slots::new_auction(Origin::ROOT, 5, 2)); assert_ok!(Slots::new_auction(Origin::ROOT, 5, 2));
@@ -1371,7 +1404,7 @@ mod tests {
run_to_block(10); run_to_block(10);
let h = BlakeTwo256::hash(&[1u8][..]); let h = BlakeTwo256::hash(&[1u8][..]);
assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, vec![1])); assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, 0.into(), h, 1, vec![1]));
assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![1])); assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), vec![1]));
assert_ok!(Slots::new_auction(Origin::ROOT, 5, 2)); assert_ok!(Slots::new_auction(Origin::ROOT, 5, 2));
@@ -1560,4 +1593,94 @@ mod tests {
]; ];
assert_eq!(Slots::calculate_winners(winning.clone(), TestParachains::new_id), winners); assert_eq!(Slots::calculate_winners(winning.clone(), TestParachains::new_id), winners);
} }
#[test]
fn deploy_code_too_large() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Slots::new_auction(Origin::ROOT, 5, 1));
assert_ok!(Slots::bid(Origin::signed(1), 0, 1, 1, 1, 5));
run_to_block(9);
assert_eq!(Slots::onboard_queue(1), vec![0.into()]);
run_to_block(10);
let code = vec![0u8; (MAX_CODE_SIZE + 1) as _];
let h = BlakeTwo256::hash(&code[..]);
assert_eq!(
Slots::fix_deploy_data(
Origin::signed(1), 0, 0.into(), h, code.len() as _, vec![1],
),
Err(Error::<Test>::CodeTooLarge.into()),
);
});
}
#[test]
fn deploy_maximum_ok() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Slots::new_auction(Origin::ROOT, 5, 1));
assert_ok!(Slots::bid(Origin::signed(1), 0, 1, 1, 1, 5));
run_to_block(9);
assert_eq!(Slots::onboard_queue(1), vec![0.into()]);
run_to_block(10);
let code = vec![0u8; MAX_CODE_SIZE as _];
let head_data = vec![1u8; MAX_HEAD_DATA_SIZE as _];
let h = BlakeTwo256::hash(&code[..]);
assert_ok!(Slots::fix_deploy_data(
Origin::signed(1), 0, 0.into(), h, code.len() as _, head_data,
));
});
}
#[test]
fn deploy_head_data_too_large() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Slots::new_auction(Origin::ROOT, 5, 1));
assert_ok!(Slots::bid(Origin::signed(1), 0, 1, 1, 1, 5));
run_to_block(9);
assert_eq!(Slots::onboard_queue(1), vec![0.into()]);
run_to_block(10);
let code = vec![0u8; MAX_CODE_SIZE as _];
let head_data = vec![1u8; (MAX_HEAD_DATA_SIZE + 1) as _];
let h = BlakeTwo256::hash(&code[..]);
assert_eq!(
Slots::fix_deploy_data(
Origin::signed(1), 0, 0.into(), h, code.len() as _, head_data,
),
Err(Error::<Test>::HeadDataTooLarge.into()),
);
});
}
#[test]
fn code_size_must_be_correct() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Slots::new_auction(Origin::ROOT, 5, 1));
assert_ok!(Slots::bid(Origin::signed(1), 0, 1, 1, 1, 5));
run_to_block(9);
assert_eq!(Slots::onboard_queue(1), vec![0.into()]);
run_to_block(10);
let code = vec![0u8; MAX_CODE_SIZE as _];
let head_data = vec![1u8; MAX_HEAD_DATA_SIZE as _];
let h = BlakeTwo256::hash(&code[..]);
assert_ok!(Slots::fix_deploy_data(
Origin::signed(1), 0, 0.into(), h, (code.len() - 1) as _, head_data,
));
assert!(Slots::elaborate_deploy_data(Origin::signed(0), 0.into(), code).is_err());
});
}
} }
+8 -1
View File
@@ -77,7 +77,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("kusama"), spec_name: create_runtime_str!("kusama"),
impl_name: create_runtime_str!("parity-kusama"), impl_name: create_runtime_str!("parity-kusama"),
authoring_version: 2, authoring_version: 2,
spec_version: 1047, spec_version: 1048,
impl_version: 0, impl_version: 0,
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
}; };
@@ -461,6 +461,11 @@ impl attestations::Trait for Runtime {
type RewardAttestation = Staking; type RewardAttestation = Staking;
} }
parameter_types! {
pub const MaxCodeSize: u32 = 10 * 1024 * 1024; // 10 MB
pub const MaxHeadDataSize: u32 = 20 * 1024; // 20 KB
}
impl parachains::Trait for Runtime { impl parachains::Trait for Runtime {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
@@ -468,6 +473,8 @@ impl parachains::Trait for Runtime {
type Randomness = RandomnessCollectiveFlip; type Randomness = RandomnessCollectiveFlip;
type ActiveParachains = Registrar; type ActiveParachains = Registrar;
type Registrar = Registrar; type Registrar = Registrar;
type MaxCodeSize = MaxCodeSize;
type MaxHeadDataSize = MaxHeadDataSize;
} }
parameter_types! { parameter_types! {
+8 -1
View File
@@ -78,7 +78,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("polkadot"), spec_name: create_runtime_str!("polkadot"),
impl_name: create_runtime_str!("parity-polkadot"), impl_name: create_runtime_str!("parity-polkadot"),
authoring_version: 2, authoring_version: 2,
spec_version: 1002, spec_version: 1003,
impl_version: 0, impl_version: 0,
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
}; };
@@ -465,6 +465,11 @@ impl attestations::Trait for Runtime {
type RewardAttestation = Staking; type RewardAttestation = Staking;
} }
parameter_types! {
pub const MaxCodeSize: u32 = 10 * 1024 * 1024; // 10 MB
pub const MaxHeadDataSize: u32 = 20 * 1024; // 20 KB
}
impl parachains::Trait for Runtime { impl parachains::Trait for Runtime {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
@@ -472,6 +477,8 @@ impl parachains::Trait for Runtime {
type Randomness = RandomnessCollectiveFlip; type Randomness = RandomnessCollectiveFlip;
type ActiveParachains = Registrar; type ActiveParachains = Registrar;
type Registrar = Registrar; type Registrar = Registrar;
type MaxCodeSize = MaxCodeSize;
type MaxHeadDataSize = MaxHeadDataSize;
} }
parameter_types! { parameter_types! {