mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 07:01:03 +00:00
A more comprehensive model for PoV-Blocks and Candidate receipts (#843)
* encode the candidate statement as only the hash * refactor CandidateReceipt and CollationInfo * introduce an abridged candidate receipt type * erasure coding stores candidate receipt * store omitted data instead and introduce AvailableData type * refactor availability-store schema * tweak schema and APIs a bit more * get availability-store tests passing * accept AbridgedCandidateReceipt in `set_heads` * change statement type in primitives to be hash-only * fix parachains runtime tests * fix bad merge * rewrite validation pipeline * remove evaluation module * use abridged candidate hash as canonical * statement table uses abridged candidate receipts * kill availability_store::Data struct * port shared table to new validation pipelines * extract full validation pipeline to helper * remove old validation pipeline from collation module * polkadot-validation compiles * polkadot-validation tests compile * make local collation available in validation service * port legacy network code * polkadot-network fully ported * network: ensure fresh statement is propagated * remove pov_block_hash from LocalValidationData * remove candidate_hash field from AttestedCandidate and update runtime * port runtimes to new ParachainHost definition * port over polkadot-collator * fix test compilation * better fix * remove unrelated validation work dispatch fix * address grumbles * fix equality check
This commit is contained in:
committed by
GitHub
parent
1f9d2af08e
commit
b7d30aa379
@@ -18,7 +18,7 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
use codec::{Encode, Decode};
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
use sp_runtime::traits::{
|
||||
Hash as HashT, BlakeTwo256, Saturating, One, Dispatchable,
|
||||
@@ -29,8 +29,9 @@ use primitives::{
|
||||
Balance,
|
||||
parachain::{
|
||||
self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, ParachainDispatchOrigin,
|
||||
UpwardMessage, ValidatorId, ActiveParas, CollatorId, Retriable,
|
||||
NEW_HEADS_IDENTIFIER,
|
||||
UpwardMessage, ValidatorId, ActiveParas, CollatorId, Retriable, OmittedValidationData,
|
||||
CandidateReceipt, GlobalValidationSchedule, AbridgedCandidateReceipt,
|
||||
LocalValidationData, NEW_HEADS_IDENTIFIER,
|
||||
},
|
||||
};
|
||||
use frame_support::{
|
||||
@@ -211,6 +212,10 @@ decl_error! {
|
||||
ParentMismatch,
|
||||
/// Head data was too large.
|
||||
HeadDataTooLarge,
|
||||
/// Para does not have enough balance to pay fees.
|
||||
CannotPayFees,
|
||||
/// Unexpected relay-parent for a candidate receipt.
|
||||
UnexpectedRelayParent,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +237,11 @@ decl_module! {
|
||||
|
||||
let mut proceeded = Vec::with_capacity(heads.len());
|
||||
|
||||
let schedule = GlobalValidationSchedule {
|
||||
max_code_size: T::MaxCodeSize::get(),
|
||||
max_head_data_size: T::MaxHeadDataSize::get(),
|
||||
};
|
||||
|
||||
if !active_parachains.is_empty() {
|
||||
// perform integrity checks before writing to storage.
|
||||
{
|
||||
@@ -256,7 +266,7 @@ decl_module! {
|
||||
|
||||
Self::check_upward_messages(
|
||||
id,
|
||||
&head.candidate.upward_messages,
|
||||
&head.candidate.commitments.upward_messages,
|
||||
MAX_QUEUE_COUNT,
|
||||
WATERMARK_QUEUE_SIZE,
|
||||
)?;
|
||||
@@ -267,7 +277,11 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
let para_blocks = Self::check_candidates(&heads, &active_parachains)?;
|
||||
let para_blocks = Self::check_candidates(
|
||||
&schedule,
|
||||
&heads,
|
||||
&active_parachains,
|
||||
)?;
|
||||
|
||||
<attestations::Module<T>>::note_included(&heads, para_blocks);
|
||||
|
||||
@@ -275,6 +289,9 @@ decl_module! {
|
||||
&heads,
|
||||
);
|
||||
|
||||
// note: we dispatch new messages _after_ the call to `check_candidates`
|
||||
// which deducts any fees. if that were not the case, an upward message
|
||||
// could be dispatched and spend money that invalidated a candidate.
|
||||
Self::dispatch_upward_messages(
|
||||
MAX_QUEUE_COUNT,
|
||||
WATERMARK_QUEUE_SIZE,
|
||||
@@ -397,7 +414,7 @@ impl<T: Trait> Module<T> {
|
||||
// Queue up upwards messages (from parachains to relay chain).
|
||||
Self::queue_upward_messages(
|
||||
id,
|
||||
&head.candidate.upward_messages,
|
||||
&head.candidate.commitments.upward_messages,
|
||||
&mut ordered_needs_dispatch,
|
||||
);
|
||||
}
|
||||
@@ -433,7 +450,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple FIFO dispatcher.
|
||||
/// Simple FIFO dispatcher. This must be called after parachain fees are checked,
|
||||
/// as dispatched messages may spend parachain funds.
|
||||
fn dispatch_upward_messages(
|
||||
max_queue_count: usize,
|
||||
watermark_queue_size: usize,
|
||||
@@ -540,18 +558,19 @@ impl<T: Trait> Module<T> {
|
||||
(DutyRoster { validator_duty: roles_val, }, orig_seed)
|
||||
}
|
||||
|
||||
/// Get the parachain status necessary for validation.
|
||||
pub fn parachain_status(id: ¶chain::Id) -> Option<parachain::Status> {
|
||||
let balance = T::ParachainCurrency::free_balance(*id);
|
||||
Self::parachain_head(id).map(|head_data| parachain::Status {
|
||||
head_data: parachain::HeadData(head_data),
|
||||
balance,
|
||||
// TODO: https://github.com/paritytech/polkadot/issues/92
|
||||
// plug in some real values here. most likely governable.
|
||||
fee_schedule: parachain::FeeSchedule {
|
||||
base: 0,
|
||||
per_byte: 0,
|
||||
}
|
||||
/// Get the global validation schedule for all parachains.
|
||||
pub fn global_validation_schedule() -> GlobalValidationSchedule {
|
||||
GlobalValidationSchedule {
|
||||
max_code_size: T::MaxCodeSize::get(),
|
||||
max_head_data_size: T::MaxHeadDataSize::get(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the local validation schedule for a particular parachain.
|
||||
pub fn local_validation_data(id: ¶chain::Id) -> Option<LocalValidationData> {
|
||||
Self::parachain_head(id).map(|parent_head| LocalValidationData {
|
||||
parent_head: primitives::parachain::HeadData(parent_head),
|
||||
balance: T::ParachainCurrency::free_balance(*id),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -563,6 +582,7 @@ impl<T: Trait> Module<T> {
|
||||
// check the attestations on these candidates. The candidates should have been checked
|
||||
// that each candidates' chain ID is valid.
|
||||
fn check_candidates(
|
||||
schedule: &GlobalValidationSchedule,
|
||||
attested_candidates: &[AttestedCandidate],
|
||||
active_parachains: &[(ParaId, Option<(CollatorId, Retriable)>)]
|
||||
) -> rstd::result::Result<IncludedBlocks<T>, sp_runtime::DispatchError>
|
||||
@@ -635,6 +655,29 @@ impl<T: Trait> Module<T> {
|
||||
sorted_duties
|
||||
};
|
||||
|
||||
// computes the omitted validation data for a particular parachain.
|
||||
let full_candidate = |abridged: &AbridgedCandidateReceipt|
|
||||
-> rstd::result::Result<CandidateReceipt, sp_runtime::DispatchError>
|
||||
{
|
||||
let para_id = abridged.parachain_index;
|
||||
let parent_head = match Self::parachain_head(¶_id)
|
||||
.map(primitives::parachain::HeadData)
|
||||
{
|
||||
Some(p) => p,
|
||||
None => Err(Error::<T>::ParentMismatch)?,
|
||||
};
|
||||
|
||||
let omitted = OmittedValidationData {
|
||||
global_validation: schedule.clone(),
|
||||
local_validation: LocalValidationData {
|
||||
parent_head,
|
||||
balance: T::ParachainCurrency::free_balance(para_id),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(abridged.clone().complete(omitted))
|
||||
};
|
||||
|
||||
let sorted_validators = make_sorted_duties(&duty_roster.validator_duty);
|
||||
|
||||
let parent_hash = <system::Module<T>>::parent_hash();
|
||||
@@ -644,18 +687,20 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
let mut para_block_hashes = Vec::new();
|
||||
|
||||
let max_head_data_size = T::MaxHeadDataSize::get();
|
||||
for candidate in attested_candidates {
|
||||
let para_id = candidate.parachain_index();
|
||||
let validator_group = validator_groups.group_for(para_id)
|
||||
.ok_or(Error::<T>::NoValidatorGroup)?;
|
||||
|
||||
let actual_head = Self::parachain_head(¶_id)
|
||||
.map(primitives::parachain::HeadData);
|
||||
|
||||
// NOTE: when changing this to allow older blocks,
|
||||
// care must be taken in the availability store pruning to ensure that
|
||||
// data is stored correctly. A block containing a candidate C can be
|
||||
// orphaned before a block containing C is finalized. Care must be taken
|
||||
// not to prune the data for C simply because an orphaned block contained
|
||||
// it.
|
||||
ensure!(
|
||||
actual_head.as_ref() == Some(&candidate.candidate.parent_head),
|
||||
Error::<T>::ParentMismatch,
|
||||
candidate.candidate().relay_parent.as_ref() == parent_hash.as_ref(),
|
||||
Error::<T>::UnexpectedRelayParent,
|
||||
);
|
||||
|
||||
ensure!(
|
||||
@@ -669,14 +714,21 @@ impl<T: Trait> Module<T> {
|
||||
);
|
||||
|
||||
ensure!(
|
||||
max_head_data_size >= candidate.candidate().head_data.0.len() as _,
|
||||
schedule.max_head_data_size >= candidate.candidate().head_data.0.len() as _,
|
||||
Error::<T>::HeadDataTooLarge,
|
||||
);
|
||||
|
||||
let fees = candidate.candidate().fees;
|
||||
let full_candidate = full_candidate(candidate.candidate())?;
|
||||
let fees = full_candidate.commitments.fees;
|
||||
|
||||
ensure!(
|
||||
full_candidate.local_validation.balance >= full_candidate.commitments.fees,
|
||||
Error::<T>::CannotPayFees,
|
||||
);
|
||||
|
||||
T::ParachainCurrency::deduct(para_id, fees)?;
|
||||
|
||||
let mut candidate_hash = None;
|
||||
let candidate_hash = candidate.candidate().hash();
|
||||
let mut encoded_implicit = None;
|
||||
let mut encoded_explicit = None;
|
||||
|
||||
@@ -702,18 +754,14 @@ impl<T: Trait> Module<T> {
|
||||
let (payload, sig) = match validity_attestation {
|
||||
ValidityAttestation::Implicit(sig) => {
|
||||
let payload = encoded_implicit.get_or_insert_with(|| localized_payload(
|
||||
Statement::Candidate(candidate.candidate.clone()),
|
||||
Statement::Candidate(candidate_hash),
|
||||
));
|
||||
|
||||
(payload, sig)
|
||||
}
|
||||
ValidityAttestation::Explicit(sig) => {
|
||||
let hash = candidate_hash
|
||||
.get_or_insert_with(|| candidate.candidate.hash())
|
||||
.clone();
|
||||
|
||||
let payload = encoded_explicit.get_or_insert_with(|| localized_payload(
|
||||
Statement::Valid(hash),
|
||||
Statement::Valid(candidate_hash),
|
||||
));
|
||||
|
||||
(payload, sig)
|
||||
@@ -726,12 +774,12 @@ impl<T: Trait> Module<T> {
|
||||
);
|
||||
}
|
||||
|
||||
para_block_hashes.push(candidate_hash.unwrap_or_else(|| candidate.candidate().hash()));
|
||||
|
||||
ensure!(
|
||||
candidate.validity_votes.len() == expected_votes_len,
|
||||
Error::<T>::UntaggedVotes
|
||||
);
|
||||
|
||||
para_block_hashes.push(candidate_hash);
|
||||
}
|
||||
|
||||
Ok(IncludedBlocks {
|
||||
@@ -833,7 +881,7 @@ mod tests {
|
||||
use primitives::{
|
||||
parachain::{
|
||||
CandidateReceipt, HeadData, ValidityAttestation, ValidatorId, Info as ParaInfo,
|
||||
Scheduling,
|
||||
Scheduling, LocalValidationData, CandidateCommitments,
|
||||
},
|
||||
BlockNumber,
|
||||
};
|
||||
@@ -981,7 +1029,7 @@ mod tests {
|
||||
type RewardRemainder = ();
|
||||
type CurrencyToVote = ();
|
||||
type Event = ();
|
||||
type Currency = balances::Module<Test>;
|
||||
type Currency = Balances;
|
||||
type Slash = ();
|
||||
type Reward = ();
|
||||
type SessionsPerEra = SessionsPerEra;
|
||||
@@ -1006,7 +1054,7 @@ mod tests {
|
||||
|
||||
impl slots::Trait for Test {
|
||||
type Event = ();
|
||||
type Currency = balances::Module<Test>;
|
||||
type Currency = Balances;
|
||||
type Parachains = registrar::Module<Test>;
|
||||
type EndingPeriod = EndingPeriod;
|
||||
type LeasePeriod = LeasePeriod;
|
||||
@@ -1022,7 +1070,7 @@ mod tests {
|
||||
impl registrar::Trait for Test {
|
||||
type Event = ();
|
||||
type Origin = Origin;
|
||||
type Currency = balances::Module<Test>;
|
||||
type Currency = Balances;
|
||||
type ParathreadDeposit = ParathreadDeposit;
|
||||
type SwapAux = slots::Module<Test>;
|
||||
type QueueSize = QueueSize;
|
||||
@@ -1037,7 +1085,7 @@ mod tests {
|
||||
impl Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type ParachainCurrency = balances::Module<Test>;
|
||||
type ParachainCurrency = Balances;
|
||||
type Randomness = RandomnessCollectiveFlip;
|
||||
type ActiveParachains = registrar::Module<Test>;
|
||||
type Registrar = registrar::Module<Test>;
|
||||
@@ -1046,6 +1094,7 @@ mod tests {
|
||||
}
|
||||
|
||||
type Parachains = Module<Test>;
|
||||
type Balances = balances::Module<Test>;
|
||||
type System = system::Module<Test>;
|
||||
type RandomnessCollectiveFlip = randomness_collective_flip::Module<Test>;
|
||||
type Registrar = registrar::Module<Test>;
|
||||
@@ -1125,6 +1174,38 @@ mod tests {
|
||||
ParachainsCall::set_heads(v)
|
||||
}
|
||||
|
||||
// creates a template candidate which pins to correct relay-chain state.
|
||||
fn raw_candidate(para_id: ParaId) -> CandidateReceipt {
|
||||
CandidateReceipt {
|
||||
parachain_index: para_id,
|
||||
relay_parent: System::parent_hash(),
|
||||
head_data: Default::default(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
pov_block_hash: Default::default(),
|
||||
global_validation: GlobalValidationSchedule {
|
||||
max_code_size: <Test as Trait>::MaxCodeSize::get(),
|
||||
max_head_data_size: <Test as Trait>::MaxHeadDataSize::get(),
|
||||
},
|
||||
local_validation: LocalValidationData {
|
||||
parent_head: HeadData(Parachains::parachain_head(¶_id).unwrap()),
|
||||
balance: <Balances as ParachainCurrency<u64>>::free_balance(para_id),
|
||||
},
|
||||
commitments: CandidateCommitments::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// makes a blank attested candidate from a `CandidateReceipt`.
|
||||
fn make_blank_attested(candidate: CandidateReceipt) -> AttestedCandidate {
|
||||
let (candidate, _) = candidate.abridge();
|
||||
|
||||
AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_attestations(candidate: &mut AttestedCandidate) {
|
||||
let mut vote_implicit = false;
|
||||
let parent_hash = <system::Module<Test>>::parent_hash();
|
||||
@@ -1150,7 +1231,7 @@ mod tests {
|
||||
let key = extract_key(authorities[idx].clone());
|
||||
|
||||
let statement = if vote_implicit {
|
||||
Statement::Candidate(candidate.candidate.clone())
|
||||
Statement::Candidate(candidate_hash.clone())
|
||||
} else {
|
||||
Statement::Valid(candidate_hash.clone())
|
||||
};
|
||||
@@ -1176,23 +1257,12 @@ mod tests {
|
||||
id: u32,
|
||||
upward_messages: Vec<(ParachainDispatchOrigin, Vec<u8>)>
|
||||
) -> AttestedCandidate {
|
||||
AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate: CandidateReceipt {
|
||||
parachain_index: id.into(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: upward_messages.into_iter()
|
||||
.map(|x| UpwardMessage { origin: x.0, data: x.1 })
|
||||
.collect(),
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}
|
||||
}
|
||||
let mut raw_candidate = raw_candidate(id.into());
|
||||
raw_candidate.commitments.upward_messages = upward_messages.into_iter()
|
||||
.map(|x| UpwardMessage { origin: x.0, data: x.1 })
|
||||
.collect();
|
||||
|
||||
make_blank_attested(raw_candidate)
|
||||
}
|
||||
|
||||
fn init_block() {
|
||||
@@ -1586,23 +1656,7 @@ mod tests {
|
||||
|
||||
new_test_ext(parachains.clone()).execute_with(|| {
|
||||
run_to_block(2);
|
||||
let candidate = AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate: CandidateReceipt {
|
||||
parachain_index: 0.into(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
erasure_root: [1u8; 32].into(),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
let candidate = make_blank_attested(raw_candidate(0.into()));
|
||||
assert!(Parachains::dispatch(set_heads(vec![candidate]), Origin::NONE).is_err());
|
||||
})
|
||||
}
|
||||
@@ -1618,37 +1672,8 @@ mod tests {
|
||||
run_to_block(2);
|
||||
assert_eq!(Parachains::active_parachains().len(), 2);
|
||||
|
||||
let mut candidate_a = AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate: CandidateReceipt {
|
||||
parachain_index: 0.into(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}
|
||||
};
|
||||
|
||||
let mut candidate_b = AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate: CandidateReceipt {
|
||||
parachain_index: 1.into(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}
|
||||
};
|
||||
let mut candidate_a = make_blank_attested(raw_candidate(0.into()));
|
||||
let mut candidate_b = make_blank_attested(raw_candidate(1.into()));
|
||||
|
||||
make_attestations(&mut candidate_a);
|
||||
make_attestations(&mut candidate_b);
|
||||
@@ -1674,22 +1699,8 @@ mod tests {
|
||||
|
||||
new_test_ext(parachains.clone()).execute_with(|| {
|
||||
run_to_block(2);
|
||||
let mut candidate = AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate: CandidateReceipt {
|
||||
parachain_index: 0.into(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}
|
||||
};
|
||||
|
||||
let mut candidate = make_blank_attested(raw_candidate(0.into()));
|
||||
make_attestations(&mut candidate);
|
||||
|
||||
let mut double_validity = candidate.clone();
|
||||
@@ -1712,22 +1723,8 @@ mod tests {
|
||||
|
||||
new_test_ext(parachains.clone()).execute_with(|| {
|
||||
run_to_block(2);
|
||||
let mut candidate = AttestedCandidate {
|
||||
validity_votes: vec![],
|
||||
validator_indices: BitVec::new(),
|
||||
candidate: CandidateReceipt {
|
||||
parachain_index: 0.into(),
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}
|
||||
};
|
||||
|
||||
let mut candidate = make_blank_attested(raw_candidate(0.into()));
|
||||
make_attestations(&mut candidate);
|
||||
|
||||
// Change the last vote index to make it not corresponding to the assigned group.
|
||||
|
||||
Reference in New Issue
Block a user