Commit be7f00a8 authored by joe petrowski's avatar joe petrowski Committed by Bastian Köcher
Browse files

Use decl_error in common runtime modules (#796)



* use decl_error in common runtime modules

* Apply suggestions from code review
Co-authored-by: default avatarBastian Köcher <bkchr@users.noreply.github.com>
parent 9cebfce3
Pipeline #75726 passed with stages
in 20 minutes
......@@ -21,7 +21,9 @@
use rstd::prelude::*;
use codec::{Encode, Decode};
use frame_support::{decl_storage, decl_module, ensure, dispatch::DispatchResult, traits::Get};
use frame_support::{
decl_storage, decl_module, decl_error, ensure, dispatch::DispatchResult, traits::Get
};
use primitives::{Hash, parachain::{AttestedCandidate, CandidateReceipt, Id as ParaId}};
use sp_runtime::RuntimeDebug;
......@@ -106,13 +108,22 @@ decl_storage! {
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
/// More attestations can be added only once in a block.
TooManyAttestations,
}
}
decl_module! {
/// Parachain-attestations module.
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
type Error = Error<T>;
/// Provide candidate receipts for parachains, in ascending order by id.
fn more_attestations(origin, _more: MoreAttestations) -> DispatchResult {
ensure_none(origin)?;
ensure!(!<DidUpdate>::exists(), "More attestations can be added only once in a block.");
ensure!(!<DidUpdate>::exists(), Error::<T>::TooManyAttestations);
<DidUpdate>::put(true);
Ok(())
......
......@@ -18,7 +18,7 @@
use rstd::prelude::*;
use sp_io::{hashing::keccak_256, crypto::secp256k1_ecdsa_recover};
use frame_support::{decl_event, decl_storage, decl_module};
use frame_support::{decl_event, decl_storage, decl_module, decl_error};
use frame_support::weights::SimpleDispatchInfo;
use frame_support::traits::{Currency, Get, VestingCurrency};
use system::{ensure_root, ensure_none};
......@@ -102,6 +102,15 @@ decl_event!(
}
);
decl_error! {
pub enum Error for Module<T: Trait> {
/// Invalid Ethereum signature.
InvalidEthereumSignature,
/// Ethereum address has no claim.
SignerHasNoClaim,
}
}
decl_storage! {
// A macro for the Storage trait, and its implementation, for this module.
// This allows for type-safe usage of the Substrate storage database, so you can
......@@ -126,6 +135,8 @@ decl_storage! {
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error<T>;
/// The Prefix that is used in signed Ethereum messages for this network
const Prefix: &[u8] = T::Prefix::get();
......@@ -139,10 +150,10 @@ decl_module! {
let data = dest.using_encoded(to_ascii_hex);
let signer = Self::eth_recover(&ethereum_signature, &data)
.ok_or("Invalid Ethereum signature")?;
.ok_or(Error::<T>::InvalidEthereumSignature)?;
let balance_due = <Claims<T>>::get(&signer)
.ok_or("Ethereum address has no claim")?;
.ok_or(Error::<T>::SignerHasNoClaim)?;
// Check if this claim should have a vesting schedule.
if let Some(vs) = <Vesting<T>>::get(&signer) {
......@@ -415,7 +426,7 @@ mod tests {
assert_eq!(Balances::free_balance(&42), 0);
assert_noop!(
Claims::claim(Origin::NONE, 69, sig(&bob(), &69u64.encode())),
"Ethereum address has no claim"
Error::<Test>::SignerHasNoClaim
);
assert_ok!(Claims::mint_claim(Origin::ROOT, eth(&bob()), 200, None));
assert_ok!(Claims::claim(Origin::NONE, 69, sig(&bob(), &69u64.encode())));
......@@ -434,7 +445,7 @@ mod tests {
assert_eq!(Balances::free_balance(&42), 0);
assert_noop!(
Claims::claim(Origin::NONE, 69, sig(&bob(), &69u64.encode())),
"Ethereum address has no claim"
Error::<Test>::SignerHasNoClaim
);
assert_ok!(Claims::mint_claim(Origin::ROOT, eth(&bob()), 200, Some((50, 10, 1))));
assert_ok!(Claims::claim(Origin::NONE, 69, sig(&bob(), &69u64.encode())));
......@@ -459,7 +470,10 @@ mod tests {
new_test_ext().execute_with(|| {
assert_eq!(Balances::free_balance(&42), 0);
assert_ok!(Claims::claim(Origin::NONE, 42, sig(&alice(), &42u64.encode())));
assert_noop!(Claims::claim(Origin::NONE, 42, sig(&alice(), &42u64.encode())), "Ethereum address has no claim");
assert_noop!(
Claims::claim(Origin::NONE, 42, sig(&alice(), &42u64.encode())),
Error::<Test>::SignerHasNoClaim
);
});
}
......@@ -467,7 +481,10 @@ mod tests {
fn non_sender_sig_doesnt_work() {
new_test_ext().execute_with(|| {
assert_eq!(Balances::free_balance(&42), 0);
assert_noop!(Claims::claim(Origin::NONE, 42, sig(&alice(), &69u64.encode())), "Ethereum address has no claim");
assert_noop!(
Claims::claim(Origin::NONE, 42, sig(&alice(), &69u64.encode())),
Error::<Test>::SignerHasNoClaim
);
});
}
......@@ -475,7 +492,10 @@ mod tests {
fn non_claimant_doesnt_work() {
new_test_ext().execute_with(|| {
assert_eq!(Balances::free_balance(&42), 0);
assert_noop!(Claims::claim(Origin::NONE, 42, sig(&bob(), &69u64.encode())), "Ethereum address has no claim");
assert_noop!(
Claims::claim(Origin::NONE, 42, sig(&bob(), &69u64.encode())),
Error::<Test>::SignerHasNoClaim
);
});
}
......
......@@ -67,7 +67,7 @@
//! funds ultimately end up in module's fund sub-account.
use frame_support::{
decl_module, decl_storage, decl_event, storage::child, ensure, traits::{
decl_module, decl_storage, decl_event, decl_error, storage::child, ensure, traits::{
Currency, Get, OnUnbalanced, WithdrawReason, ExistenceRequirement::AllowDeath
}
};
......@@ -187,8 +187,56 @@ decl_event! {
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
/// Last slot must be greater than first slot.
LastSlotBeforeFirstSlot,
/// The last slot cannot be more then 3 slots after the first slot.
LastSlotTooFarInFuture,
/// The campaign ends before the current block number. The end must be in the future.
CannotEndInPast,
/// There was an overflow.
Overflow,
/// The contribution was below the minimum, `MinContribution`.
ContributionTooSmall,
/// Invalid fund index.
InvalidFundIndex,
/// Contributions exceed maximum amount.
CapExceeded,
/// The contribution period has already ended.
ContributionPeriodOver,
/// The origin of this call is invalid.
InvalidOrigin,
/// Deployment data for a fund can only be set once. The deployment data for this fund
/// already exists.
ExistingDeployData,
/// Deployment data has not been set for this fund.
UnsetDeployData,
/// This fund has already been onboarded.
AlreadyOnboard,
/// This crowdfund does not correspond to a parachain.
NotParachain,
/// This parachain still has its deposit. Implies that it has already been offboarded.
ParaHasDeposit,
/// Funds have not yet been returned.
FundsNotReturned,
/// Fund has not yet retired.
FundNotRetired,
/// The crowdfund has not yet ended.
FundNotEnded,
/// There are no contributions stored in this crowdfund.
NoContributions,
/// This crowdfund has an active parachain and cannot be dissolved.
HasActiveParachain,
/// The retirement period has not ended.
InRetirementPeriod,
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error<T>;
fn deposit_event() = default;
/// Create a new crowdfunding campaign for a parachain slot deposit for the current auction.
......@@ -201,16 +249,16 @@ decl_module! {
) {
let owner = ensure_signed(origin)?;
ensure!(first_slot < last_slot, "last slot must be greater than first slot");
ensure!(last_slot <= first_slot + 3.into(), "last slot cannot be more then 3 more than first slot");
ensure!(end > <system::Module<T>>::block_number(), "end must be in the future");
ensure!(first_slot < last_slot, Error::<T>::LastSlotBeforeFirstSlot);
ensure!(last_slot <= first_slot + 3.into(), Error::<T>::LastSlotTooFarInFuture);
ensure!(end > <system::Module<T>>::block_number(), Error::<T>::CannotEndInPast);
let deposit = T::SubmissionDeposit::get();
let transfer = WithdrawReason::Transfer.into();
let imb = T::Currency::withdraw(&owner, deposit, transfer, AllowDeath)?;
let index = FundCount::get();
let next_index = index.checked_add(1).ok_or("overflow when adding fund")?;
let next_index = index.checked_add(1).ok_or(Error::<T>::Overflow)?;
FundCount::put(next_index);
// No fees are paid here if we need to create this account; that's why we don't just
......@@ -239,14 +287,14 @@ decl_module! {
fn contribute(origin, #[compact] index: FundIndex, #[compact] value: BalanceOf<T>) {
let who = ensure_signed(origin)?;
ensure!(value >= T::MinContribution::get(), "contribution too small");
let mut fund = Self::funds(index).ok_or("invalid fund index")?;
fund.raised = fund.raised.checked_add(&value).ok_or("overflow when adding new funds")?;
ensure!(fund.raised <= fund.cap, "contributions exceed cap");
ensure!(value >= T::MinContribution::get(), Error::<T>::ContributionTooSmall);
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
fund.raised = fund.raised.checked_add(&value).ok_or(Error::<T>::Overflow)?;
ensure!(fund.raised <= fund.cap, Error::<T>::CapExceeded);
// Make sure crowdfund has not ended
let now = <system::Module<T>>::block_number();
ensure!(fund.end > now, "contribution period ended");
ensure!(fund.end > now, Error::<T>::ContributionPeriodOver);
T::Currency::transfer(&who, &Self::fund_account_id(index), value, AllowDeath)?;
......@@ -301,9 +349,9 @@ decl_module! {
) {
let who = ensure_signed(origin)?;
let mut fund = Self::funds(index).ok_or("invalid fund index")?;
ensure!(fund.owner == who, "origin must be fund owner");
ensure!(fund.deploy_data.is_none(), "deploy data already set");
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
ensure!(fund.owner == who, Error::<T>::InvalidOrigin); // must be fund owner
ensure!(fund.deploy_data.is_none(), Error::<T>::ExistingDeployData);
fund.deploy_data = Some((code_hash, initial_head_data));
......@@ -324,9 +372,9 @@ decl_module! {
) {
let _ = ensure_signed(origin)?;
let mut fund = Self::funds(index).ok_or("invalid fund index")?;
let (code_hash, initial_head_data) = fund.clone().deploy_data.ok_or("deploy data not fixed")?;
ensure!(fund.parachain.is_none(), "fund already onboarded");
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)?;
ensure!(fund.parachain.is_none(), Error::<T>::AlreadyOnboard);
fund.parachain = Some(para_id);
let fund_origin = system::RawOrigin::Signed(Self::fund_account_id(index)).into();
......@@ -341,13 +389,13 @@ decl_module! {
fn begin_retirement(origin, #[compact] index: FundIndex) {
let _ = ensure_signed(origin)?;
let mut fund = Self::funds(index).ok_or("invalid fund index")?;
let parachain_id = fund.parachain.take().ok_or("fund has no parachain")?;
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
let parachain_id = fund.parachain.take().ok_or(Error::<T>::NotParachain)?;
// No deposit information implies the parachain was off-boarded
ensure!(<slots::Module<T>>::deposits(parachain_id).len() == 0, "parachain still has deposit");
ensure!(<slots::Module<T>>::deposits(parachain_id).len() == 0, Error::<T>::ParaHasDeposit);
let account = Self::fund_account_id(index);
// Funds should be returned at the end of off-boarding
ensure!(T::Currency::free_balance(&account) >= fund.raised, "funds not yet returned");
ensure!(T::Currency::free_balance(&account) >= fund.raised, Error::<T>::FundsNotReturned);
// This fund just ended. Withdrawal period begins.
let now = <system::Module<T>>::block_number();
......@@ -362,15 +410,15 @@ decl_module! {
fn withdraw(origin, #[compact] index: FundIndex) {
let who = ensure_signed(origin)?;
let mut fund = Self::funds(index).ok_or("invalid fund index")?;
ensure!(fund.parachain.is_none(), "fund has not retired");
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
ensure!(fund.parachain.is_none(), Error::<T>::FundNotRetired);
let now = <system::Module<T>>::block_number();
// `fund.end` can represent the end of a failed crowdsale or the beginning of retirement
ensure!(now >= fund.end, "fund has not ended");
ensure!(now >= fund.end, Error::<T>::FundNotEnded);
let balance = Self::contribution_get(index, &who);
ensure!(balance > Zero::zero(), "no contributions stored");
ensure!(balance > Zero::zero(), Error::<T>::NoContributions);
// Avoid using transfer to ensure we don't pay any fees.
let fund_account = &Self::fund_account_id(index);
......@@ -392,12 +440,12 @@ decl_module! {
fn dissolve(origin, #[compact] index: FundIndex) {
let _ = ensure_signed(origin)?;
let fund = Self::funds(index).ok_or("invalid fund index")?;
ensure!(fund.parachain.is_none(), "cannot dissolve fund with active parachain");
let fund = Self::funds(index).ok_or(Error::<T>::InvalidFundIndex)?;
ensure!(fund.parachain.is_none(), Error::<T>::HasActiveParachain);
let now = <system::Module<T>>::block_number();
ensure!(
now >= fund.end.saturating_add(T::RetirementPeriod::get()),
"retirement period not over"
Error::<T>::InRetirementPeriod
);
let account = Self::fund_account_id(index);
......@@ -673,6 +721,7 @@ mod tests {
type Crowdfund = Module<Test>;
type RandomnessCollectiveFlip = randomness_collective_flip::Module<Test>;
use balances::Error as BalancesError;
use slots::Error as SlotsError;
// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
......@@ -749,11 +798,20 @@ mod tests {
fn create_handles_basic_errors() {
new_test_ext().execute_with(|| {
// Cannot create a crowdfund with bad slots
assert_noop!(Crowdfund::create(Origin::signed(1), 1000, 4, 1, 9), "last slot must be greater than first slot");
assert_noop!(Crowdfund::create(Origin::signed(1), 1000, 1, 5, 9), "last slot cannot be more then 3 more than first slot");
assert_noop!(
Crowdfund::create(Origin::signed(1), 1000, 4, 1, 9),
Error::<Test>::LastSlotBeforeFirstSlot
);
assert_noop!(
Crowdfund::create(Origin::signed(1), 1000, 1, 5, 9),
Error::<Test>::LastSlotTooFarInFuture
);
// Cannot create a crowdfund without some deposit funds
assert_noop!(Crowdfund::create(Origin::signed(1337), 1000, 1, 3, 9), BalancesError::<Test, _>::InsufficientBalance);
assert_noop!(
Crowdfund::create(Origin::signed(1337), 1000, 1, 3, 9),
BalancesError::<Test, _>::InsufficientBalance
);
});
}
......@@ -791,22 +849,22 @@ mod tests {
fn contribute_handles_basic_errors() {
new_test_ext().execute_with(|| {
// Cannot contribute to non-existing fund
assert_noop!(Crowdfund::contribute(Origin::signed(1), 0, 49), "invalid fund index");
assert_noop!(Crowdfund::contribute(Origin::signed(1), 0, 49), Error::<Test>::InvalidFundIndex);
// Cannot contribute below minimum contribution
assert_noop!(Crowdfund::contribute(Origin::signed(1), 0, 9), "contribution too small");
assert_noop!(Crowdfund::contribute(Origin::signed(1), 0, 9), Error::<Test>::ContributionTooSmall);
// Set up a crowdfund
assert_ok!(Crowdfund::create(Origin::signed(1), 1000, 1, 4, 9));
assert_ok!(Crowdfund::contribute(Origin::signed(1), 0, 101));
// Cannot contribute past the limit
assert_noop!(Crowdfund::contribute(Origin::signed(2), 0, 900), "contributions exceed cap");
assert_noop!(Crowdfund::contribute(Origin::signed(2), 0, 900), Error::<Test>::CapExceeded);
// Move past end date
run_to_block(10);
// Cannot contribute to ended fund
assert_noop!(Crowdfund::contribute(Origin::signed(1), 0, 49), "contribution period ended");
assert_noop!(Crowdfund::contribute(Origin::signed(1), 0, 49), Error::<Test>::ContributionPeriodOver);
});
}
......@@ -845,7 +903,7 @@ mod tests {
0,
<Test as system::Trait>::Hash::default(),
vec![0]),
"origin must be fund owner"
Error::<Test>::InvalidOrigin
);
// Cannot set deploy data to an invalid index
......@@ -854,7 +912,7 @@ mod tests {
1,
<Test as system::Trait>::Hash::default(),
vec![0]),
"invalid fund index"
Error::<Test>::InvalidFundIndex
);
// Cannot set deploy data after it already has been set
......@@ -870,7 +928,7 @@ mod tests {
0,
<Test as system::Trait>::Hash::default(),
vec![1]),
"deploy data already set"
Error::<Test>::ExistingDeployData
);
});
}
......@@ -924,9 +982,9 @@ mod tests {
run_to_block(10);
// Cannot onboard invalid fund index
assert_noop!(Crowdfund::onboard(Origin::signed(1), 1, 0.into()), "invalid fund index");
assert_noop!(Crowdfund::onboard(Origin::signed(1), 1, 0.into()), Error::<Test>::InvalidFundIndex);
// Cannot onboard crowdfund without deploy data
assert_noop!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()), "deploy data not fixed");
assert_noop!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()), Error::<Test>::UnsetDeployData);
// Add deploy data
assert_ok!(Crowdfund::fix_deploy_data(
......@@ -937,13 +995,13 @@ mod tests {
));
// Cannot onboard fund with incorrect parachain id
assert_noop!(Crowdfund::onboard(Origin::signed(1), 0, 1.into()), "parachain id not in onboarding");
assert_noop!(Crowdfund::onboard(Origin::signed(1), 0, 1.into()), SlotsError::<Test>::ParaNotOnboarding);
// Onboard crowdfund
assert_ok!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()));
// Cannot onboard fund again
assert_noop!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()), "fund already onboarded");
assert_noop!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()), Error::<Test>::AlreadyOnboard);
});
}
......@@ -1011,7 +1069,7 @@ mod tests {
run_to_block(10);
// Cannot retire fund that is not onboarded
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 0), "fund has no parachain");
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 0), Error::<Test>::NotParachain);
// Onboard crowdfund
assert_ok!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()));
......@@ -1020,16 +1078,16 @@ mod tests {
assert_eq!(fund.parachain, Some(0.into()));
// Cannot retire fund whose deposit has not been returned
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 0), "parachain still has deposit");
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 0), Error::<Test>::ParaHasDeposit);
run_to_block(50);
// Cannot retire invalid fund index
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 1), "invalid fund index");
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);
// Cannot retire twice
assert_ok!(Crowdfund::begin_retirement(Origin::signed(1), 0));
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 0), "fund has no parachain");
assert_noop!(Crowdfund::begin_retirement(Origin::signed(1), 0), Error::<Test>::NotParachain);
});
}
......@@ -1072,14 +1130,14 @@ mod tests {
run_to_block(5);
// Cannot withdraw before fund ends
assert_noop!(Crowdfund::withdraw(Origin::signed(1), 0), "fund has not ended");
assert_noop!(Crowdfund::withdraw(Origin::signed(1), 0), Error::<Test>::FundNotEnded);
run_to_block(10);
// Cannot withdraw if they did not contribute
assert_noop!(Crowdfund::withdraw(Origin::signed(2), 0), "no contributions stored");
assert_noop!(Crowdfund::withdraw(Origin::signed(2), 0), Error::<Test>::NoContributions);
// Cannot withdraw from a non-existent fund
assert_noop!(Crowdfund::withdraw(Origin::signed(1), 1), "invalid fund index");
assert_noop!(Crowdfund::withdraw(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);
});
}
......@@ -1130,9 +1188,9 @@ mod tests {
assert_ok!(Crowdfund::contribute(Origin::signed(3), 0, 300));
// Cannot dissolve an invalid fund index
assert_noop!(Crowdfund::dissolve(Origin::signed(1), 1), "invalid fund index");
assert_noop!(Crowdfund::dissolve(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);
// Cannot dissolve a fund in progress
assert_noop!(Crowdfund::dissolve(Origin::signed(1), 0), "retirement period not over");
assert_noop!(Crowdfund::dissolve(Origin::signed(1), 0), Error::<Test>::InRetirementPeriod);
run_to_block(10);
......@@ -1146,7 +1204,7 @@ mod tests {
assert_ok!(Crowdfund::onboard(Origin::signed(1), 0, 0.into()));
// Cannot dissolve an active fund
assert_noop!(Crowdfund::dissolve(Origin::signed(1), 0), "cannot dissolve fund with active parachain");
assert_noop!(Crowdfund::dissolve(Origin::signed(1), 0), Error::<Test>::HasActiveParachain);
});
}
......
......@@ -35,7 +35,7 @@ use primitives::{
},
};
use frame_support::{
Parameter, dispatch::DispatchResult, decl_storage, decl_module, ensure,
Parameter, dispatch::DispatchResult, decl_storage, decl_module, decl_error, ensure,
traits::{Currency, Get, WithdrawReason, ExistenceRequirement, Randomness},
};
......@@ -182,10 +182,10 @@ decl_storage! {
/// The ordered list of ParaIds that have a `RelayDispatchQueue` entry.
NeedsDispatch: Vec<ParaId>;
/// Some if the parachain heads get updated in this block, along with the parachain IDs that
/// did update. Ordered in the same way as `registrar::Active` (i.e. by ParaId).
/// `Some` if the parachain heads get updated in this block, along with the parachain IDs
/// that did update. Ordered in the same way as `registrar::Active` (i.e. by ParaId).
///
/// None if not yet updated.
/// `None` if not yet updated.
pub DidUpdate: Option<Vec<ParaId>>;
}
add_extra_genesis {
......@@ -194,19 +194,60 @@ decl_storage! {
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
/// Parachain heads must be updated only once in the block.
TooManyHeadUpdates,
/// Too many parachain candidates.
TooManyParaCandidates,
/// Proposed heads must be ascending order by parachain ID without duplicate.
HeadsOutOfOrder,
/// Candidate is for an unregistered parachain.
UnregisteredPara,
/// Invalid collator.
InvalidCollator,
/// The message queue is full. Messages will be added when there is space.
QueueFull,
/// The message origin is invalid.
InvalidMessageOrigin,
/// Egress routes should be in ascending order by parachain ID without duplicates.
EgressOutOfOrder,
/// A parachain cannot route a message to itself.
SelfAddressed,
/// The trie root cannot be empty.
EmptyTrieRoot,
/// Cannot route to a non-existing parachain.
DestinationDoesNotExist,
/// No validator group for parachain.
NoValidatorGroup,
/// Not enough validity votes for candidate.
NotEnoughValidityVotes,
/// The number of attestations exceeds the number of authorities.
VotesExceedsAuthorities,
/// Attesting validator not on this chain's validation duty.
WrongValidatorAttesting,
/// Invalid signature from attester.
InvalidSignature,
/// Extra untagged validity votes along with candidate.
UntaggedVotes,
}
}
decl_module! {
/// Parachains module.
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
type Error = Error<T>;
/// Provide candidate receipts for parachains, in ascending order by id.
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
pub fn set_heads(origin, heads: Vec<AttestedCandidate>) -> DispatchResult {
ensure_none(origin)?;
ensure!(!<DidUpdate>::exists(), "Parachain heads must be updated only once in the block");
ensure!(!<DidUpdate>::exists(), Error::<T>::TooManyHeadUpdates);
let active_parachains = Self::active_parachains();
let parachain_count = active_parachains.len();
ensure!(heads.len() <= parachain_count, "Too many parachain candidates");
ensure!(heads.len() <= parachain_count, Error::<T>::TooManyParaCandidates);
let mut proceeded = Vec::with_capacity(heads.len());
......@@ -221,15 +262,15 @@ decl_module! {
// proposed heads must be ascending order by parachain ID without duplicate.
ensure!(
last_id.as_ref().map_or(true, |x| x < &id),
"candidate out of order"
Error::<T>::HeadsOutOfOrder
);
// must be unknown since active parachains are always sorted.
let (_, maybe_required_collator) = iter.find(|para| para.0 == id