mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 16:47:57 +00:00
Include a reference to the validation data in the candidate descriptor (#1442)
* rename GlobalValidationSchedule to GlobalValidationData * guide: update candidate descriptor to contain validation data hash * guide: add note in inclusion module about checking validation data hash * primitives: update CandidateDescriptor to contain new hash * fix payload computation * add helpers for computing validation data to runtime modules * guide: note routines * inclusion: check validation data hash and fix local_validation_data bug * add a case to candidate_checks and improve that test substantially * bump versions * address review comments * add a test for including code upgrade * bump kusama version * bump westend & polkadot versions
This commit is contained in:
committed by
GitHub
parent
1ed17cd467
commit
09f602f8de
@@ -58,7 +58,7 @@ use sp_core::Pair;
|
||||
use polkadot_primitives::v0::{
|
||||
BlockId, Hash, Block, DownwardMessage,
|
||||
BlockData, DutyRoster, HeadData, Id as ParaId,
|
||||
PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationSchedule,
|
||||
PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationData,
|
||||
Collation, CollationInfo, collator_signature_payload,
|
||||
};
|
||||
use polkadot_cli::{
|
||||
@@ -148,7 +148,7 @@ pub trait ParachainContext: Clone {
|
||||
fn produce_candidate(
|
||||
&mut self,
|
||||
relay_parent: Hash,
|
||||
global_validation: GlobalValidationSchedule,
|
||||
global_validation: GlobalValidationData,
|
||||
local_validation: LocalValidationData,
|
||||
downward_messages: Vec<DownwardMessage>,
|
||||
) -> Self::ProduceCandidate;
|
||||
@@ -158,7 +158,7 @@ pub trait ParachainContext: Clone {
|
||||
pub async fn collate<P>(
|
||||
relay_parent: Hash,
|
||||
local_id: ParaId,
|
||||
global_validation: GlobalValidationSchedule,
|
||||
global_validation: GlobalValidationData,
|
||||
local_validation_data: LocalValidationData,
|
||||
downward_messages: Vec<DownwardMessage>,
|
||||
mut para_context: P,
|
||||
@@ -315,7 +315,7 @@ fn build_collator_service<P, C, R, Extrinsic>(
|
||||
|
||||
let work = future::lazy(move |_| {
|
||||
let api = client.runtime_api();
|
||||
let global_validation = try_fr!(api.global_validation_schedule(&id));
|
||||
let global_validation = try_fr!(api.global_validation_data(&id));
|
||||
let local_validation = match try_fr!(api.local_validation_data(&id, para_id)) {
|
||||
Some(local_validation) => local_validation,
|
||||
None => return future::Either::Left(future::ok(())),
|
||||
@@ -477,7 +477,7 @@ mod tests {
|
||||
fn produce_candidate(
|
||||
&mut self,
|
||||
_relay_parent: Hash,
|
||||
_global: GlobalValidationSchedule,
|
||||
_global: GlobalValidationData,
|
||||
_local_validation: LocalValidationData,
|
||||
_: Vec<DownwardMessage>,
|
||||
) -> Self::ProduceCandidate {
|
||||
|
||||
@@ -21,7 +21,7 @@ use polkadot_primitives::v0::{
|
||||
Block,
|
||||
Id as ParaId, Chain, DutyRoster, ParachainHost, ValidatorId,
|
||||
Retriable, CollatorId, AbridgedCandidateReceipt,
|
||||
GlobalValidationSchedule, LocalValidationData, ErasureChunk, SigningContext,
|
||||
GlobalValidationData, LocalValidationData, ErasureChunk, SigningContext,
|
||||
PoVBlock, BlockData, ValidationCode,
|
||||
};
|
||||
use polkadot_validation::{SharedTable, TableRouter};
|
||||
@@ -180,7 +180,7 @@ sp_api::mock_impl_runtime_apis! {
|
||||
Some(ValidationCode(Vec::new()))
|
||||
}
|
||||
|
||||
fn global_validation_schedule() -> GlobalValidationSchedule {
|
||||
fn global_validation_data() -> GlobalValidationData {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
|
||||
@@ -606,7 +606,7 @@ impl CandidateBackingJob {
|
||||
with_commitments: impl FnOnce(CandidateCommitments) -> Result<T, E>,
|
||||
) -> Result<Result<T, E>, Error> {
|
||||
let omitted_validation = OmittedValidationData {
|
||||
global_validation: outputs.global_validation_schedule,
|
||||
global_validation: outputs.global_validation_data,
|
||||
local_validation: outputs.local_validation_data,
|
||||
};
|
||||
|
||||
@@ -773,7 +773,7 @@ mod tests {
|
||||
use futures::{executor, future, Future};
|
||||
use polkadot_primitives::v1::{
|
||||
AssignmentKind, BlockData, CandidateCommitments, CollatorId, CoreAssignment, CoreIndex,
|
||||
LocalValidationData, GlobalValidationSchedule, GroupIndex, HeadData,
|
||||
LocalValidationData, GlobalValidationData, GroupIndex, HeadData,
|
||||
ValidatorPair, ValidityAttestation,
|
||||
};
|
||||
use polkadot_subsystem::{
|
||||
@@ -792,7 +792,7 @@ mod tests {
|
||||
keystore: KeyStorePtr,
|
||||
validators: Vec<Sr25519Keyring>,
|
||||
validator_public: Vec<ValidatorId>,
|
||||
global_validation_schedule: GlobalValidationSchedule,
|
||||
global_validation_data: GlobalValidationData,
|
||||
local_validation_data: LocalValidationData,
|
||||
roster: SchedulerRoster,
|
||||
head_data: HashMap<ParaId, HeadData>,
|
||||
@@ -877,7 +877,7 @@ mod tests {
|
||||
validation_code_hash: Default::default(),
|
||||
};
|
||||
|
||||
let global_validation_schedule = GlobalValidationSchedule {
|
||||
let global_validation_data = GlobalValidationData {
|
||||
max_code_size: 1000,
|
||||
max_head_data_size: 1000,
|
||||
block_number: Default::default(),
|
||||
@@ -891,7 +891,7 @@ mod tests {
|
||||
roster,
|
||||
head_data,
|
||||
local_validation_data,
|
||||
global_validation_schedule,
|
||||
global_validation_data,
|
||||
signing_context,
|
||||
relay_parent,
|
||||
}
|
||||
@@ -921,7 +921,7 @@ mod tests {
|
||||
|
||||
fn make_erasure_root(test: &TestState, pov: PoV) -> Hash {
|
||||
let omitted_validation = OmittedValidationData {
|
||||
global_validation: test.global_validation_schedule.clone(),
|
||||
global_validation: test.global_validation_data.clone(),
|
||||
local_validation: test.local_validation_data.clone(),
|
||||
};
|
||||
|
||||
@@ -1048,7 +1048,7 @@ mod tests {
|
||||
) if pov == pov && &c == candidate.descriptor() => {
|
||||
tx.send(Ok(
|
||||
ValidationResult::Valid(ValidationOutputs {
|
||||
global_validation_schedule: test_state.global_validation_schedule,
|
||||
global_validation_data: test_state.global_validation_data,
|
||||
local_validation_data: test_state.local_validation_data,
|
||||
head_data: expected_head_data.clone(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -1160,7 +1160,7 @@ mod tests {
|
||||
) if pov == pov && &c == candidate_a.descriptor() => {
|
||||
tx.send(Ok(
|
||||
ValidationResult::Valid(ValidationOutputs {
|
||||
global_validation_schedule: test_state.global_validation_schedule,
|
||||
global_validation_data: test_state.global_validation_data,
|
||||
local_validation_data: test_state.local_validation_data,
|
||||
head_data: expected_head_data.clone(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -1281,7 +1281,7 @@ mod tests {
|
||||
) if pov == pov && &c == candidate_a.descriptor() => {
|
||||
tx.send(Ok(
|
||||
ValidationResult::Valid(ValidationOutputs {
|
||||
global_validation_schedule: test_state.global_validation_schedule,
|
||||
global_validation_data: test_state.global_validation_data,
|
||||
local_validation_data: test_state.local_validation_data,
|
||||
head_data: expected_head_data.clone(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -1438,7 +1438,7 @@ mod tests {
|
||||
) if pov == pov && &c == candidate_b.descriptor() => {
|
||||
tx.send(Ok(
|
||||
ValidationResult::Valid(ValidationOutputs {
|
||||
global_validation_schedule: test_state.global_validation_schedule,
|
||||
global_validation_data: test_state.global_validation_data,
|
||||
local_validation_data: test_state.local_validation_data,
|
||||
head_data: expected_head_data.clone(),
|
||||
upward_messages: Vec::new(),
|
||||
|
||||
@@ -24,7 +24,7 @@ use parity_scale_codec::{Decode, Encode};
|
||||
use polkadot_primitives::v1::{
|
||||
Hash, CommittedCandidateReceipt, CandidateReceipt, CompactStatement,
|
||||
EncodeAs, Signed, SigningContext, ValidatorIndex, ValidatorId,
|
||||
UpwardMessage, Balance, ValidationCode, GlobalValidationSchedule, LocalValidationData,
|
||||
UpwardMessage, Balance, ValidationCode, GlobalValidationData, LocalValidationData,
|
||||
HeadData,
|
||||
};
|
||||
use polkadot_statement_table::{
|
||||
@@ -118,7 +118,7 @@ pub struct ValidationOutputs {
|
||||
/// The head-data produced by validation.
|
||||
pub head_data: HeadData,
|
||||
/// The global validation schedule.
|
||||
pub global_validation_schedule: GlobalValidationSchedule,
|
||||
pub global_validation_data: GlobalValidationData,
|
||||
/// The local validation data.
|
||||
pub local_validation_data: LocalValidationData,
|
||||
/// Upward messages to the relay chain.
|
||||
|
||||
@@ -24,7 +24,7 @@ use sp_core::Pair;
|
||||
use codec::{Encode, Decode};
|
||||
use primitives::v0::{
|
||||
Hash, DownwardMessage,
|
||||
HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationSchedule,
|
||||
HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationData,
|
||||
};
|
||||
use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli};
|
||||
use parking_lot::Mutex;
|
||||
@@ -58,7 +58,7 @@ impl ParachainContext for AdderContext {
|
||||
fn produce_candidate(
|
||||
&mut self,
|
||||
_relay_parent: Hash,
|
||||
_global_validation: GlobalValidationSchedule,
|
||||
_global_validation: GlobalValidationData,
|
||||
local_validation: LocalValidationData,
|
||||
_: Vec<DownwardMessage>,
|
||||
) -> Self::ProduceCandidate
|
||||
|
||||
@@ -179,7 +179,7 @@ pub struct DutyRoster {
|
||||
/// These are global parameters that apply to all parachain candidates in a block.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Default))]
|
||||
pub struct GlobalValidationSchedule<N = BlockNumber> {
|
||||
pub struct GlobalValidationData<N = BlockNumber> {
|
||||
/// The maximum code size permitted, in bytes.
|
||||
pub max_code_size: u32,
|
||||
/// The maximum head-data size permitted, in bytes.
|
||||
@@ -278,7 +278,7 @@ pub struct CandidateReceipt<H = Hash, N = BlockNumber> {
|
||||
/// The hash of the PoV-block.
|
||||
pub pov_block_hash: H,
|
||||
/// The global validation schedule.
|
||||
pub global_validation: GlobalValidationSchedule<N>,
|
||||
pub global_validation: GlobalValidationData<N>,
|
||||
/// The local validation data.
|
||||
pub local_validation: LocalValidationData<N>,
|
||||
/// Commitments made as a result of validation.
|
||||
@@ -352,7 +352,7 @@ impl Ord for CandidateReceipt {
|
||||
#[cfg_attr(feature = "std", derive(Debug, Default))]
|
||||
pub struct OmittedValidationData<N = BlockNumber> {
|
||||
/// The global validation schedule.
|
||||
pub global_validation: GlobalValidationSchedule<N>,
|
||||
pub global_validation: GlobalValidationData<N>,
|
||||
/// The local validation data.
|
||||
pub local_validation: LocalValidationData<N>,
|
||||
}
|
||||
@@ -762,7 +762,7 @@ sp_api::decl_runtime_apis! {
|
||||
fn active_parachains() -> Vec<(Id, Option<(CollatorId, Retriable)>)>;
|
||||
/// Get the global validation schedule that all parachains should
|
||||
/// be validated under.
|
||||
fn global_validation_schedule() -> GlobalValidationSchedule;
|
||||
fn global_validation_data() -> GlobalValidationData;
|
||||
/// Get the local validation data for a particular parachain.
|
||||
fn local_validation_data(id: Id) -> Option<LocalValidationData>;
|
||||
/// Get the given parachain's head code blob.
|
||||
|
||||
@@ -60,14 +60,16 @@ pub const INCLUSION_INHERENT_IDENTIFIER: InherentIdentifier = *b"inclusn0";
|
||||
pub fn collator_signature_payload<H: AsRef<[u8]>>(
|
||||
relay_parent: &H,
|
||||
para_id: &Id,
|
||||
validation_data_hash: &Hash,
|
||||
pov_hash: &Hash,
|
||||
) -> [u8; 68] {
|
||||
) -> [u8; 100] {
|
||||
// 32-byte hash length is protected in a test below.
|
||||
let mut payload = [0u8; 68];
|
||||
let mut payload = [0u8; 100];
|
||||
|
||||
payload[0..32].copy_from_slice(relay_parent.as_ref());
|
||||
u32::from(*para_id).using_encoded(|s| payload[32..32 + s.len()].copy_from_slice(s));
|
||||
payload[36..68].copy_from_slice(pov_hash.as_ref());
|
||||
payload[36..68].copy_from_slice(validation_data_hash.as_ref());
|
||||
payload[68..100].copy_from_slice(pov_hash.as_ref());
|
||||
|
||||
payload
|
||||
}
|
||||
@@ -75,11 +77,18 @@ pub fn collator_signature_payload<H: AsRef<[u8]>>(
|
||||
fn check_collator_signature<H: AsRef<[u8]>>(
|
||||
relay_parent: &H,
|
||||
para_id: &Id,
|
||||
validation_data_hash: &Hash,
|
||||
pov_hash: &Hash,
|
||||
collator: &CollatorId,
|
||||
signature: &CollatorSignature,
|
||||
) -> Result<(),()> {
|
||||
let payload = collator_signature_payload(relay_parent, para_id, pov_hash);
|
||||
let payload = collator_signature_payload(
|
||||
relay_parent,
|
||||
para_id,
|
||||
validation_data_hash,
|
||||
pov_hash,
|
||||
);
|
||||
|
||||
if signature.verify(&payload[..], collator) {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -87,6 +96,14 @@ fn check_collator_signature<H: AsRef<[u8]>>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the `validation_data_hash` from global & local validation data.
|
||||
pub fn validation_data_hash<N: Encode>(
|
||||
global: &GlobalValidationData<N>,
|
||||
local: &LocalValidationData<N>,
|
||||
) -> Hash {
|
||||
BlakeTwo256::hash_of(&(global, local))
|
||||
}
|
||||
|
||||
/// A unique descriptor of the candidate receipt.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Default))]
|
||||
@@ -97,11 +114,16 @@ pub struct CandidateDescriptor<H = Hash> {
|
||||
pub relay_parent: H,
|
||||
/// The collator's sr25519 public key.
|
||||
pub collator: CollatorId,
|
||||
/// Signature on blake2-256 of components of this receipt:
|
||||
/// The parachain index, the relay parent, and the pov_hash.
|
||||
pub signature: CollatorSignature,
|
||||
/// The blake2-256 hash of the validation data. This is extra data derived from
|
||||
/// relay-chain state which may vary based on bitfields included before the candidate.
|
||||
/// Thus it cannot be derived entirely from the relay-parent.
|
||||
pub validation_data_hash: Hash,
|
||||
/// The blake2-256 hash of the pov.
|
||||
pub pov_hash: Hash,
|
||||
/// Signature on blake2-256 of components of this receipt:
|
||||
/// The parachain index, the relay parent, the validation data hash, and the pov_hash.
|
||||
pub signature: CollatorSignature,
|
||||
|
||||
}
|
||||
|
||||
impl<H: AsRef<[u8]>> CandidateDescriptor<H> {
|
||||
@@ -110,6 +132,7 @@ impl<H: AsRef<[u8]>> CandidateDescriptor<H> {
|
||||
check_collator_signature(
|
||||
&self.relay_parent,
|
||||
&self.para_id,
|
||||
&self.validation_data_hash,
|
||||
&self.pov_hash,
|
||||
&self.collator,
|
||||
&self.signature,
|
||||
@@ -146,7 +169,7 @@ pub struct FullCandidateReceipt<H = Hash, N = BlockNumber> {
|
||||
/// The inner candidate receipt.
|
||||
pub inner: CandidateReceipt<H>,
|
||||
/// The global validation schedule.
|
||||
pub global_validation: GlobalValidationSchedule<N>,
|
||||
pub global_validation: GlobalValidationData<N>,
|
||||
/// The local validation data.
|
||||
pub local_validation: LocalValidationData<N>,
|
||||
}
|
||||
@@ -232,7 +255,7 @@ pub struct LocalValidationData<N = BlockNumber> {
|
||||
/// These are global parameters that apply to all candidates in a block.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Default))]
|
||||
pub struct GlobalValidationSchedule<N = BlockNumber> {
|
||||
pub struct GlobalValidationData<N = BlockNumber> {
|
||||
/// The maximum code size permitted, in bytes.
|
||||
pub max_code_size: u32,
|
||||
/// The maximum head-data size permitted, in bytes.
|
||||
@@ -465,7 +488,7 @@ impl CoreAssignment {
|
||||
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
|
||||
pub struct OmittedValidationData {
|
||||
/// The global validation schedule.
|
||||
pub global_validation: GlobalValidationSchedule,
|
||||
pub global_validation: GlobalValidationData,
|
||||
/// The local validation data.
|
||||
pub local_validation: LocalValidationData,
|
||||
}
|
||||
@@ -636,9 +659,9 @@ sp_api::decl_runtime_apis! {
|
||||
/// cores can have paras assigned to them.
|
||||
fn availability_cores() -> Vec<CoreState<N>>;
|
||||
|
||||
/// Yields the GlobalValidationSchedule. This applies to all para candidates with the
|
||||
/// Yields the GlobalValidationData. This applies to all para candidates with the
|
||||
/// relay-parent equal to the block in which context this is invoked in.
|
||||
fn global_validation_schedule() -> GlobalValidationSchedule<N>;
|
||||
fn global_validation_data() -> GlobalValidationData<N>;
|
||||
|
||||
/// Yields the LocalValidationData for the given ParaId along with an assumption that
|
||||
/// should be used if the para currently occupies a core.
|
||||
@@ -696,4 +719,18 @@ mod tests {
|
||||
assert_eq!(info.next_rotation_at(), 0);
|
||||
assert_eq!(info.last_rotation_at(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collator_signature_payload_is_valid() {
|
||||
// if this fails, collator signature verification code has to be updated.
|
||||
let h = Hash::default();
|
||||
assert_eq!(h.as_ref().len(), 32);
|
||||
|
||||
let _payload = collator_signature_payload(
|
||||
&Hash::from([1; 32]),
|
||||
&5u32.into(),
|
||||
&Hash::from([2; 32]),
|
||||
&Hash::from([3; 32]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +137,10 @@ enum CoreState {
|
||||
|
||||
## Global Validation Schedule
|
||||
|
||||
Yields the [`GlobalValidationSchedule`](../types/candidate.md#globalvalidationschedule) at the state of a given block. This applies to all para candidates with the relay-parent equal to that block.
|
||||
Yields the [`GlobalValidationData`](../types/candidate.md#globalvalidationschedule) at the state of a given block. This applies to all para candidates with the relay-parent equal to that block.
|
||||
|
||||
```rust
|
||||
fn global_validation_schedule(at: Block) -> GlobalValidationSchedule;
|
||||
fn global_validation_data(at: Block) -> GlobalValidationData;
|
||||
```
|
||||
|
||||
## Local Validation Data
|
||||
|
||||
@@ -35,6 +35,9 @@ fn update_configuration(f: impl FnOnce(&mut HostConfiguration)) {
|
||||
*pending = Some(x);
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the GlobalValidationData, assuming the context is the parent block.
|
||||
fn global_validation_data() -> GlobalValidationData;
|
||||
```
|
||||
|
||||
## Entry-points
|
||||
|
||||
@@ -62,6 +62,7 @@ All failed checks should lead to an unrecoverable error making the block invalid
|
||||
1. check that each candidate corresponds to a scheduled core and that they are ordered in the same order the cores appear in assignments in `scheduled`.
|
||||
1. check that `scheduled` is sorted ascending by `CoreIndex`, without duplicates.
|
||||
1. check that there is no candidate pending availability for any scheduled `ParaId`.
|
||||
1. check that each candidate's `validation_data_hash` corresponds to a `(LocalValidationData, GlobalValidationData)` computed from the current state.
|
||||
1. If the core assignment includes a specific collator, ensure the backed candidate is issued by that collator.
|
||||
1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_frequency` of `Paras::last_code_upgrade(para_id, true)`, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID.
|
||||
1. Check the collator's signature on the candidate data.
|
||||
|
||||
@@ -112,6 +112,7 @@ OutgoingParas: Vec<ParaId>;
|
||||
* `is_parathread(ParaId) -> bool`: Returns true if the para ID references any live parathread.
|
||||
|
||||
* `last_code_upgrade(id: ParaId, include_future: bool) -> Option<BlockNumber>`: The block number of the last scheduled upgrade of the requested para. Includes future upgrades if the flag is set. This is the `expected_at` number, not the `activated_at` number.
|
||||
* `local_validation_data(id: ParaId) -> Option<LocalValidationData>`: Get the LocalValidationData of the given para, assuming the context is the parent block. Returns `None` if the para is not known.
|
||||
|
||||
## Finalization
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ struct PoV(Vec<u8>);
|
||||
|
||||
Validation data that is often omitted from types describing candidates as it can be derived from the relay-parent of the candidate. However, with the expectation of state pruning, these are best kept available elsewhere as well.
|
||||
|
||||
This contains the [`GlobalValidationSchedule`](candidate.md#globalvalidationschedule) and [`LocalValidationData`](candidate.md#localvalidationdata)
|
||||
This contains the [`GlobalValidationData`](candidate.md#globalvalidationschedule) and [`LocalValidationData`](candidate.md#localvalidationdata)
|
||||
|
||||
```rust
|
||||
struct OmittedValidationData {
|
||||
/// The global validation schedule.
|
||||
global_validation: GlobalValidationSchedule,
|
||||
global_validation: GlobalValidationData,
|
||||
/// The local validation data.
|
||||
local_validation: LocalValidationData,
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ struct CandidateReceipt {
|
||||
|
||||
## Full Candidate Receipt
|
||||
|
||||
This is the full receipt type. The `GlobalValidationSchedule` and the `LocalValidationData` are technically redundant with the `inner.relay_parent`, which uniquely describes the a block in the blockchain from whose state these values are derived. The [`CandidateReceipt`](#candidate-receipt) variant is often used instead for this reason.
|
||||
This is the full receipt type. The `GlobalValidationData` and the `LocalValidationData` are technically redundant with the `inner.relay_parent`, which uniquely describes the a block in the blockchain from whose state these values are derived. The [`CandidateReceipt`](#candidate-receipt) variant is often used instead for this reason.
|
||||
|
||||
However, the Full Candidate Receipt type is useful as a means of avoiding the implicit dependency on availability of old blockchain state. In situations such as availability and approval, having the full description of the candidate within a self-contained struct is convenient.
|
||||
|
||||
@@ -42,7 +42,7 @@ However, the Full Candidate Receipt type is useful as a means of avoiding the im
|
||||
struct FullCandidateReceipt {
|
||||
inner: CandidateReceipt,
|
||||
/// The global validation schedule.
|
||||
global_validation: GlobalValidationSchedule,
|
||||
global_validation: GlobalValidationData,
|
||||
/// The local validation data.
|
||||
local_validation: LocalValidationData,
|
||||
}
|
||||
@@ -77,16 +77,19 @@ struct CandidateDescriptor {
|
||||
relay_parent: Hash,
|
||||
/// The collator's sr25519 public key.
|
||||
collator: CollatorId,
|
||||
/// Signature on blake2-256 of components of this receipt:
|
||||
/// The parachain index, the relay parent, and the pov_hash.
|
||||
signature: CollatorSignature,
|
||||
/// The blake2-256 hash of the validation data. These are extra parameters
|
||||
/// derived from relay-chain state that influence the validity of the block.
|
||||
validation_data_hash: Hash,
|
||||
/// The blake2-256 hash of the pov-block.
|
||||
pov_hash: Hash,
|
||||
/// Signature on blake2-256 of components of this receipt:
|
||||
/// The parachain index, the relay parent, the validation data hash, and the pov_hash.
|
||||
signature: CollatorSignature,
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## GlobalValidationSchedule
|
||||
## GlobalValidationData
|
||||
|
||||
The global validation schedule comprises of information describing the global environment for para execution, as derived from a particular relay-parent. These are parameters that will apply to all parablocks executed in the context of this relay-parent.
|
||||
|
||||
@@ -95,7 +98,7 @@ The global validation schedule comprises of information describing the global en
|
||||
/// to fully validate the candidate.
|
||||
///
|
||||
/// These are global parameters that apply to all candidates in a block.
|
||||
struct GlobalValidationSchedule {
|
||||
struct GlobalValidationData {
|
||||
/// The maximum code size permitted, in bytes.
|
||||
max_code_size: u32,
|
||||
/// The maximum head-data size permitted, in bytes.
|
||||
@@ -197,7 +200,7 @@ struct ValidationOutputs {
|
||||
/// The head-data produced by validation.
|
||||
head_data: HeadData,
|
||||
/// The global validation schedule.
|
||||
global_validation_schedule: GlobalValidationSchedule,
|
||||
global_validation_data: GlobalValidationData,
|
||||
/// The local validation data.
|
||||
local_validation_data: LocalValidationData,
|
||||
/// Upwards messages to the relay chain.
|
||||
|
||||
@@ -255,7 +255,7 @@ enum RuntimeApiRequest {
|
||||
/// Get the validation code for a specific para, using the given occupied core assumption.
|
||||
ValidationCode(ParaId, OccupiedCoreAssumption, ResponseChannel<Option<ValidationCode>>),
|
||||
/// Get the global validation schedule at the state of a given block.
|
||||
GlobalValidationSchedule(ResponseChannel<GlobalValidationSchedule>),
|
||||
GlobalValidationData(ResponseChannel<GlobalValidationData>),
|
||||
/// Get the local validation data for a specific para, with the given occupied core assumption.
|
||||
LocalValidationData(
|
||||
ParaId,
|
||||
|
||||
@@ -41,7 +41,7 @@ use primitives::v0::{
|
||||
Balance, BlockNumber,
|
||||
Id as ParaId, Chain, DutyRoster, AttestedCandidate, CompactStatement as Statement, ParachainDispatchOrigin,
|
||||
UpwardMessage, ValidatorId, ActiveParas, CollatorId, Retriable, OmittedValidationData,
|
||||
CandidateReceipt, GlobalValidationSchedule, AbridgedCandidateReceipt,
|
||||
CandidateReceipt, GlobalValidationData, AbridgedCandidateReceipt,
|
||||
LocalValidationData, Scheduling, ValidityAttestation, NEW_HEADS_IDENTIFIER, PARACHAIN_KEY_TYPE_ID,
|
||||
ValidatorSignature, SigningContext, HeadData, ValidationCode,
|
||||
Remark, DownwardMessage
|
||||
@@ -601,7 +601,7 @@ decl_module! {
|
||||
|
||||
let mut proceeded = Vec::with_capacity(heads.len());
|
||||
|
||||
let schedule = Self::global_validation_schedule();
|
||||
let schedule = Self::global_validation_data();
|
||||
|
||||
if !active_parachains.is_empty() {
|
||||
// perform integrity checks before writing to storage.
|
||||
@@ -1168,9 +1168,9 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Get the global validation schedule for all parachains.
|
||||
pub fn global_validation_schedule() -> GlobalValidationSchedule {
|
||||
pub fn global_validation_data() -> GlobalValidationData {
|
||||
let now = <system::Module<T>>::block_number();
|
||||
GlobalValidationSchedule {
|
||||
GlobalValidationData {
|
||||
max_code_size: T::MaxCodeSize::get(),
|
||||
max_head_data_size: T::MaxHeadDataSize::get(),
|
||||
block_number: T::BlockNumberConversion::convert(if now.is_zero() {
|
||||
@@ -1322,7 +1322,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,
|
||||
schedule: &GlobalValidationData,
|
||||
attested_candidates: &[AttestedCandidate],
|
||||
active_parachains: &[(ParaId, Option<(CollatorId, Retriable)>)]
|
||||
) -> sp_std::result::Result<IncludedBlocks<T>, sp_runtime::DispatchError> {
|
||||
@@ -2157,7 +2157,7 @@ mod tests {
|
||||
collator: Default::default(),
|
||||
signature: Default::default(),
|
||||
pov_block_hash: Default::default(),
|
||||
global_validation: Parachains::global_validation_schedule(),
|
||||
global_validation: Parachains::global_validation_data(),
|
||||
local_validation: Parachains::current_local_validation_data(¶_id).unwrap(),
|
||||
commitments: CandidateCommitments::default(),
|
||||
}
|
||||
|
||||
@@ -1070,7 +1070,7 @@ mod tests {
|
||||
collator: collator.public(),
|
||||
signature: pov_block_hash.using_encoded(|d| collator.sign(d)),
|
||||
pov_block_hash,
|
||||
global_validation: Parachains::global_validation_schedule(),
|
||||
global_validation: Parachains::global_validation_data(),
|
||||
local_validation: Parachains::current_local_validation_data(&id).unwrap(),
|
||||
commitments: CandidateCommitments {
|
||||
fees: 0,
|
||||
|
||||
@@ -87,7 +87,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("kusama"),
|
||||
impl_name: create_runtime_str!("parity-kusama"),
|
||||
authoring_version: 2,
|
||||
spec_version: 2019,
|
||||
spec_version: 2020,
|
||||
impl_version: 0,
|
||||
#[cfg(not(feature = "disable-runtime-api"))]
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
@@ -1115,8 +1115,8 @@ sp_api::impl_runtime_apis! {
|
||||
fn active_parachains() -> Vec<(parachain::Id, Option<(parachain::CollatorId, parachain::Retriable)>)> {
|
||||
Registrar::active_paras()
|
||||
}
|
||||
fn global_validation_schedule() -> parachain::GlobalValidationSchedule {
|
||||
Parachains::global_validation_schedule()
|
||||
fn global_validation_data() -> parachain::GlobalValidationData {
|
||||
Parachains::global_validation_data()
|
||||
}
|
||||
fn local_validation_data(id: parachain::Id) -> Option<parachain::LocalValidationData> {
|
||||
Parachains::current_local_validation_data(&id)
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
//! Configuration can change only at session boundaries and is buffered until then.
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use primitives::v1::ValidatorId;
|
||||
use primitives::v1::{ValidatorId, GlobalValidationData};
|
||||
use frame_support::{
|
||||
decl_storage, decl_module, decl_error,
|
||||
dispatch::DispatchResult,
|
||||
weights::{DispatchClass, Weight},
|
||||
};
|
||||
use sp_runtime::traits::One;
|
||||
use codec::{Encode, Decode};
|
||||
use system::ensure_root;
|
||||
|
||||
@@ -219,6 +220,16 @@ impl<T: Trait> Module<T> {
|
||||
<Self as Store>::PendingConfig::set(Some(prev));
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the global validation-data, assuming the context of the parent block.
|
||||
pub(crate) fn global_validation_data() -> GlobalValidationData<T::BlockNumber> {
|
||||
let config = Self::config();
|
||||
GlobalValidationData {
|
||||
max_code_size: config.max_code_size,
|
||||
max_head_data_size: config.max_head_data_size,
|
||||
block_number: <system::Module<T>>::block_number() - One::one(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use primitives::v1::{
|
||||
validation_data_hash,
|
||||
ValidatorId, CandidateCommitments, CandidateDescriptor, ValidatorIndex, Id as ParaId,
|
||||
AvailabilityBitfield as AvailabilityBitfield, SignedAvailabilityBitfields, SigningContext,
|
||||
BackedCandidate, CoreIndex, GroupIndex, CoreAssignment, CommittedCandidateReceipt,
|
||||
@@ -145,6 +146,8 @@ decl_error! {
|
||||
InvalidBacking,
|
||||
/// Collator did not sign PoV.
|
||||
NotCollatorSigned,
|
||||
/// The validation data hash does not match expected.
|
||||
ValidationDataHashMismatch,
|
||||
/// Internal error only returned when compiled with debug assertions.
|
||||
InternalError,
|
||||
}
|
||||
@@ -399,14 +402,21 @@ impl<T: Trait> Module<T> {
|
||||
Error::<T>::CandidateNotInParentContext,
|
||||
);
|
||||
|
||||
let code_upgrade_allowed = <paras::Module<T>>::last_code_upgrade(para_id, true)
|
||||
.map_or(
|
||||
true,
|
||||
|last| last <= relay_parent_number &&
|
||||
relay_parent_number.saturating_sub(last) >= config.validation_upgrade_frequency,
|
||||
);
|
||||
// if any, the code upgrade attempt is allowed.
|
||||
let valid_upgrade_attempt =
|
||||
candidate.candidate.commitments.new_validation_code.is_none() ||
|
||||
<paras::Module<T>>::last_code_upgrade(para_id, true)
|
||||
.map_or(
|
||||
true,
|
||||
|last| last <= relay_parent_number &&
|
||||
relay_parent_number.saturating_sub(last)
|
||||
>= config.validation_upgrade_frequency,
|
||||
);
|
||||
|
||||
ensure!(code_upgrade_allowed, Error::<T>::PrematureCodeUpgrade);
|
||||
ensure!(
|
||||
valid_upgrade_attempt,
|
||||
Error::<T>::PrematureCodeUpgrade,
|
||||
);
|
||||
ensure!(
|
||||
candidate.descriptor().check_collator_signature().is_ok(),
|
||||
Error::<T>::NotCollatorSigned,
|
||||
@@ -423,6 +433,32 @@ impl<T: Trait> Module<T> {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// this should never fail because the para is registered
|
||||
let (global_validation_data, local_validation_data) = (
|
||||
<configuration::Module<T>>::global_validation_data(),
|
||||
match <paras::Module<T>>::local_validation_data(para_id) {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
// We don't want to error out here because it will
|
||||
// brick the relay-chain. So we return early without
|
||||
// doing anything.
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let expected = validation_data_hash(
|
||||
&global_validation_data,
|
||||
&local_validation_data,
|
||||
);
|
||||
|
||||
ensure!(
|
||||
expected == candidate.descriptor().validation_data_hash,
|
||||
Error::<T>::ValidationDataHashMismatch,
|
||||
);
|
||||
}
|
||||
|
||||
ensure!(
|
||||
<PendingAvailability<T>>::get(¶_id).is_none() &&
|
||||
<PendingAvailabilityCommitments>::get(¶_id).is_none(),
|
||||
@@ -686,6 +722,7 @@ mod tests {
|
||||
let payload = primitives::v1::collator_signature_payload(
|
||||
&candidate.descriptor.relay_parent,
|
||||
&candidate.descriptor.para_id,
|
||||
&candidate.descriptor.validation_data_hash,
|
||||
&candidate.descriptor.pov_hash,
|
||||
);
|
||||
|
||||
@@ -814,6 +851,7 @@ mod tests {
|
||||
head_data: HeadData,
|
||||
pov_hash: Hash,
|
||||
relay_parent: Hash,
|
||||
validation_data_hash: Hash,
|
||||
new_validation_code: Option<ValidationCode>,
|
||||
}
|
||||
|
||||
@@ -824,6 +862,7 @@ mod tests {
|
||||
para_id: self.para_id,
|
||||
pov_hash: self.pov_hash,
|
||||
relay_parent: self.relay_parent,
|
||||
validation_data_hash: self.validation_data_hash,
|
||||
..Default::default()
|
||||
},
|
||||
commitments: CandidateCommitments {
|
||||
@@ -835,6 +874,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_vdata_hash(para_id: ParaId) -> Option<Hash> {
|
||||
let global_validation_data = Configuration::global_validation_data();
|
||||
let local_validation_data = Paras::local_validation_data(para_id)?;
|
||||
Some(validation_data_hash(&global_validation_data, &local_validation_data))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collect_pending_cleans_up_pending() {
|
||||
let chain_a = ParaId::from(1);
|
||||
@@ -1261,6 +1306,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1276,11 +1322,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::UnscheduledCandidate.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidates out of order.
|
||||
@@ -1289,12 +1338,14 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
let mut candidate_b = TestCandidateBuilder {
|
||||
para_id: chain_b,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([2; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_b).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1324,11 +1375,15 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed_b, backed_a],
|
||||
vec![chain_a_assignment.clone(), chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
// out-of-order manifests as unscheduled.
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed_b, backed_a],
|
||||
vec![chain_a_assignment.clone(), chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::UnscheduledCandidate.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate not backed.
|
||||
@@ -1337,6 +1392,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1352,11 +1408,14 @@ mod tests {
|
||||
BackingKind::Lacking,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::InsufficientBacking.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate not in parent context.
|
||||
@@ -1368,6 +1427,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: wrong_parent_hash,
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1383,11 +1443,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::CandidateNotInParentContext.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate has wrong collator.
|
||||
@@ -1396,6 +1459,7 @@ mod tests {
|
||||
para_id: thread_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(thread_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1413,15 +1477,18 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![
|
||||
chain_a_assignment.clone(),
|
||||
chain_b_assignment.clone(),
|
||||
thread_a_assignment.clone(),
|
||||
],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![
|
||||
chain_a_assignment.clone(),
|
||||
chain_b_assignment.clone(),
|
||||
thread_a_assignment.clone(),
|
||||
],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::WrongCollator.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate not well-signed by collator.
|
||||
@@ -1430,6 +1497,7 @@ mod tests {
|
||||
para_id: thread_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(thread_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1450,11 +1518,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![thread_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![thread_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::NotCollatorSigned.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// para occupied - reject.
|
||||
@@ -1463,6 +1534,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1489,11 +1561,14 @@ mod tests {
|
||||
});
|
||||
<PendingAvailabilityCommitments>::insert(&chain_a, candidate.commitments);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::CandidateScheduledBeforeParaFree.into()),
|
||||
);
|
||||
|
||||
<PendingAvailability<Test>>::remove(&chain_a);
|
||||
<PendingAvailabilityCommitments>::remove(&chain_a);
|
||||
@@ -1505,6 +1580,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1524,11 +1600,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::CandidateScheduledBeforeParaFree.into()),
|
||||
);
|
||||
|
||||
<PendingAvailabilityCommitments>::remove(&chain_a);
|
||||
}
|
||||
@@ -1540,6 +1619,7 @@ mod tests {
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
new_validation_code: Some(vec![5, 6, 7, 8].into()),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1564,11 +1644,47 @@ mod tests {
|
||||
|
||||
assert_eq!(Paras::last_code_upgrade(chain_a, true), Some(10));
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![thread_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::PrematureCodeUpgrade.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// Bad validation data hash - reject
|
||||
{
|
||||
let mut candidate = TestCandidateBuilder {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: [42u8; 32].into(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
collator_sign_candidate(
|
||||
Sr25519Keyring::One,
|
||||
&mut candidate,
|
||||
);
|
||||
|
||||
let backed = back_candidate(
|
||||
candidate,
|
||||
&validators,
|
||||
group_validators(GroupIndex::from(0)).unwrap().as_ref(),
|
||||
&signing_context,
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::ValidationDataHashMismatch.into()),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1634,6 +1750,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1645,6 +1762,7 @@ mod tests {
|
||||
para_id: chain_b,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([2; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_b).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1656,6 +1774,7 @@ mod tests {
|
||||
para_id: thread_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([3; 32]),
|
||||
validation_data_hash: make_vdata_hash(thread_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1746,6 +1865,91 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_include_candidate_with_ok_code_upgrade() {
|
||||
let chain_a = ParaId::from(1);
|
||||
|
||||
let paras = vec![(chain_a, true)];
|
||||
let validators = vec![
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
Sr25519Keyring::Dave,
|
||||
Sr25519Keyring::Ferdie,
|
||||
];
|
||||
let validator_public = validator_pubkeys(&validators);
|
||||
|
||||
new_test_ext(genesis_config(paras)).execute_with(|| {
|
||||
Validators::set(validator_public.clone());
|
||||
CurrentSessionIndex::set(5);
|
||||
|
||||
run_to_block(5, |_| None);
|
||||
|
||||
let signing_context = SigningContext {
|
||||
parent_hash: System::parent_hash(),
|
||||
session_index: 5,
|
||||
};
|
||||
|
||||
let group_validators = |group_index: GroupIndex| match group_index {
|
||||
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1, 2, 3, 4]),
|
||||
_ => panic!("Group index out of bounds for 1 parachain"),
|
||||
};
|
||||
|
||||
let chain_a_assignment = CoreAssignment {
|
||||
core: CoreIndex::from(0),
|
||||
para_id: chain_a,
|
||||
kind: AssignmentKind::Parachain,
|
||||
group_idx: GroupIndex::from(0),
|
||||
};
|
||||
|
||||
let mut candidate_a = TestCandidateBuilder {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
new_validation_code: Some(vec![1, 2, 3].into()),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
Sr25519Keyring::One,
|
||||
&mut candidate_a,
|
||||
);
|
||||
|
||||
let backed_a = back_candidate(
|
||||
candidate_a.clone(),
|
||||
&validators,
|
||||
group_validators(GroupIndex::from(0)).unwrap().as_ref(),
|
||||
&signing_context,
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
let occupied_cores = Inclusion::process_candidates(
|
||||
vec![backed_a],
|
||||
vec![
|
||||
chain_a_assignment.clone(),
|
||||
],
|
||||
&group_validators,
|
||||
).expect("candidates scheduled, in order, and backed");
|
||||
|
||||
assert_eq!(occupied_cores, vec![CoreIndex::from(0)]);
|
||||
|
||||
assert_eq!(
|
||||
<PendingAvailability<Test>>::get(&chain_a),
|
||||
Some(CandidatePendingAvailability {
|
||||
core: CoreIndex::from(0),
|
||||
descriptor: candidate_a.descriptor,
|
||||
availability_votes: default_availability_votes(),
|
||||
relay_parent_number: System::block_number() - 1,
|
||||
backed_in_number: System::block_number(),
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
<PendingAvailabilityCommitments>::get(&chain_a),
|
||||
Some(candidate_a.commitments),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_change_wipes_and_updates_session_info() {
|
||||
let chain_a = ParaId::from(1);
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::marker::PhantomData;
|
||||
use sp_runtime::traits::One;
|
||||
use sp_runtime::traits::{One, BlakeTwo256, Hash as HashT, Saturating};
|
||||
use primitives::v1::{
|
||||
Id as ParaId, ValidationCode, HeadData,
|
||||
Id as ParaId, ValidationCode, HeadData, LocalValidationData,
|
||||
};
|
||||
use frame_support::{
|
||||
decl_storage, decl_module, decl_error,
|
||||
@@ -536,6 +536,37 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
Self::past_code_meta(&id).most_recent_change()
|
||||
}
|
||||
|
||||
/// Compute the local-validation data based on the head of the para. This assumes the
|
||||
/// relay-parent is the parent of the current block.
|
||||
pub(crate) fn local_validation_data(para_id: ParaId) -> Option<LocalValidationData<T::BlockNumber>> {
|
||||
let relay_parent_number = <system::Module<T>>::block_number() - One::one();
|
||||
|
||||
let config = <configuration::Module<T>>::config();
|
||||
let freq = config.validation_upgrade_frequency;
|
||||
let delay = config.validation_upgrade_delay;
|
||||
|
||||
let last_code_upgrade = Self::last_code_upgrade(para_id, true);
|
||||
let can_upgrade_code = last_code_upgrade.map_or(
|
||||
true,
|
||||
|l| { l <= relay_parent_number && relay_parent_number.saturating_sub(l) >= freq },
|
||||
);
|
||||
|
||||
let code_upgrade_allowed = if can_upgrade_code {
|
||||
Some(relay_parent_number + delay)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some(LocalValidationData {
|
||||
parent_head: Self::para_head(¶_id)?,
|
||||
balance: 0,
|
||||
validation_code_hash: BlakeTwo256::hash_of(
|
||||
&Self::current_code(¶_id)?
|
||||
),
|
||||
code_upgrade_allowed,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
//! functions.
|
||||
|
||||
use primitives::v1::{
|
||||
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, GlobalValidationSchedule,
|
||||
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, GlobalValidationData,
|
||||
Id as ParaId, OccupiedCoreAssumption, LocalValidationData, SessionIndex, ValidationCode,
|
||||
CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex,
|
||||
GroupIndex, CandidateEvent,
|
||||
};
|
||||
use sp_runtime::traits::{One, BlakeTwo256, Hash as HashT, Saturating, Zero};
|
||||
use sp_runtime::traits::Zero;
|
||||
use frame_support::debug;
|
||||
use crate::{initializer, inclusion, scheduler, configuration, paras};
|
||||
|
||||
@@ -160,16 +160,11 @@ pub fn availability_cores<T: initializer::Trait>() -> Vec<CoreState<T::BlockNumb
|
||||
core_states
|
||||
}
|
||||
|
||||
/// Implementation for the `global_validation_schedule` function of the runtime API.
|
||||
pub fn global_validation_schedule<T: initializer::Trait>()
|
||||
-> GlobalValidationSchedule<T::BlockNumber>
|
||||
/// Implementation for the `global_validation_data` function of the runtime API.
|
||||
pub fn global_validation_data<T: initializer::Trait>()
|
||||
-> GlobalValidationData<T::BlockNumber>
|
||||
{
|
||||
let config = <configuration::Module<T>>::config();
|
||||
GlobalValidationSchedule {
|
||||
max_code_size: config.max_code_size,
|
||||
max_head_data_size: config.max_head_data_size,
|
||||
block_number: <system::Module<T>>::block_number() - One::one(),
|
||||
}
|
||||
<configuration::Module<T>>::global_validation_data()
|
||||
}
|
||||
|
||||
/// Implementation for the `local_validation_data` function of the runtime API.
|
||||
@@ -177,46 +172,19 @@ pub fn local_validation_data<T: initializer::Trait>(
|
||||
para_id: ParaId,
|
||||
assumption: OccupiedCoreAssumption,
|
||||
) -> Option<LocalValidationData<T::BlockNumber>> {
|
||||
let construct = || {
|
||||
let relay_parent_number = <system::Module<T>>::block_number() - One::one();
|
||||
|
||||
let config = <configuration::Module<T>>::config();
|
||||
let freq = config.validation_upgrade_frequency;
|
||||
let delay = config.validation_upgrade_delay;
|
||||
|
||||
let last_code_upgrade = <paras::Module<T>>::last_code_upgrade(para_id, true)?;
|
||||
let can_upgrade_code = last_code_upgrade <= relay_parent_number
|
||||
&& relay_parent_number.saturating_sub(last_code_upgrade) >= freq;
|
||||
|
||||
let code_upgrade_allowed = if can_upgrade_code {
|
||||
Some(relay_parent_number + delay)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some(LocalValidationData {
|
||||
parent_head: <paras::Module<T>>::para_head(¶_id)?,
|
||||
balance: 0,
|
||||
validation_code_hash: BlakeTwo256::hash_of(
|
||||
&<paras::Module<T>>::current_code(¶_id)?
|
||||
),
|
||||
code_upgrade_allowed,
|
||||
})
|
||||
};
|
||||
|
||||
match assumption {
|
||||
OccupiedCoreAssumption::Included => {
|
||||
<inclusion::Module<T>>::force_enact(para_id);
|
||||
construct()
|
||||
<paras::Module<T>>::local_validation_data(para_id)
|
||||
}
|
||||
OccupiedCoreAssumption::TimedOut => {
|
||||
construct()
|
||||
<paras::Module<T>>::local_validation_data(para_id)
|
||||
}
|
||||
OccupiedCoreAssumption::Free => {
|
||||
if <inclusion::Module<T>>::pending_availability(para_id).is_some() {
|
||||
None
|
||||
} else {
|
||||
construct()
|
||||
<paras::Module<T>>::local_validation_data(para_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("polkadot"),
|
||||
impl_name: create_runtime_str!("parity-polkadot"),
|
||||
authoring_version: 0,
|
||||
spec_version: 19,
|
||||
spec_version: 20,
|
||||
impl_version: 0,
|
||||
#[cfg(not(feature = "disable-runtime-api"))]
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
@@ -1256,8 +1256,8 @@ sp_api::impl_runtime_apis! {
|
||||
fn active_parachains() -> Vec<(parachain::Id, Option<(parachain::CollatorId, parachain::Retriable)>)> {
|
||||
Registrar::active_paras()
|
||||
}
|
||||
fn global_validation_schedule() -> parachain::GlobalValidationSchedule {
|
||||
Parachains::global_validation_schedule()
|
||||
fn global_validation_data() -> parachain::GlobalValidationData {
|
||||
Parachains::global_validation_data()
|
||||
}
|
||||
fn local_validation_data(id: parachain::Id) -> Option<parachain::LocalValidationData> {
|
||||
Parachains::current_local_validation_data(&id)
|
||||
|
||||
@@ -676,8 +676,8 @@ sp_api::impl_runtime_apis! {
|
||||
fn active_parachains() -> Vec<(parachain::Id, Option<(parachain::CollatorId, parachain::Retriable)>)> {
|
||||
Registrar::active_paras()
|
||||
}
|
||||
fn global_validation_schedule() -> parachain::GlobalValidationSchedule {
|
||||
Parachains::global_validation_schedule()
|
||||
fn global_validation_data() -> parachain::GlobalValidationData {
|
||||
Parachains::global_validation_data()
|
||||
}
|
||||
fn local_validation_data(id: parachain::Id) -> Option<parachain::LocalValidationData> {
|
||||
Parachains::current_local_validation_data(&id)
|
||||
|
||||
@@ -83,7 +83,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("westend"),
|
||||
impl_name: create_runtime_str!("parity-westend"),
|
||||
authoring_version: 2,
|
||||
spec_version: 39,
|
||||
spec_version: 40,
|
||||
impl_version: 0,
|
||||
#[cfg(not(feature = "disable-runtime-api"))]
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
@@ -897,8 +897,8 @@ sp_api::impl_runtime_apis! {
|
||||
fn active_parachains() -> Vec<(parachain::Id, Option<(parachain::CollatorId, parachain::Retriable)>)> {
|
||||
Registrar::active_paras()
|
||||
}
|
||||
fn global_validation_schedule() -> parachain::GlobalValidationSchedule {
|
||||
Parachains::global_validation_schedule()
|
||||
fn global_validation_data() -> parachain::GlobalValidationData {
|
||||
Parachains::global_validation_data()
|
||||
}
|
||||
fn local_validation_data(id: parachain::Id) -> Option<parachain::LocalValidationData> {
|
||||
Parachains::current_local_validation_data(&id)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
use codec::Encode;
|
||||
use polkadot_erasure_coding as erasure;
|
||||
use polkadot_primitives::v0::{
|
||||
CollationInfo, PoVBlock, LocalValidationData, GlobalValidationSchedule, OmittedValidationData,
|
||||
CollationInfo, PoVBlock, LocalValidationData, GlobalValidationData, OmittedValidationData,
|
||||
AvailableData, FeeSchedule, CandidateCommitments, ErasureChunk, ParachainHost,
|
||||
Id as ParaId, AbridgedCandidateReceipt, ValidationCode,
|
||||
};
|
||||
@@ -95,7 +95,7 @@ impl FullOutput {
|
||||
/// validation are needed, call `full_output`. Otherwise, safely drop this value.
|
||||
pub struct ValidatedCandidate<'a> {
|
||||
pov_block: &'a PoVBlock,
|
||||
global_validation: &'a GlobalValidationSchedule,
|
||||
global_validation: &'a GlobalValidationData,
|
||||
local_validation: &'a LocalValidationData,
|
||||
upward_messages: Vec<UpwardMessage>,
|
||||
fees: Balance,
|
||||
@@ -189,7 +189,7 @@ pub fn validate<'a>(
|
||||
collation: &'a CollationInfo,
|
||||
pov_block: &'a PoVBlock,
|
||||
local_validation: &'a LocalValidationData,
|
||||
global_validation: &'a GlobalValidationSchedule,
|
||||
global_validation: &'a GlobalValidationData,
|
||||
validation_code: &ValidationCode,
|
||||
) -> Result<ValidatedCandidate<'a>, Error> {
|
||||
if collation.head_data.0.len() > global_validation.max_head_data_size as _ {
|
||||
@@ -249,7 +249,7 @@ pub fn validate<'a>(
|
||||
|
||||
/// Extracts validation parameters from a Polkadot runtime API for a specific parachain.
|
||||
pub fn validation_params<P>(api: &P, relay_parent: Hash, para_id: ParaId)
|
||||
-> Result<(LocalValidationData, GlobalValidationSchedule, ValidationCode), Error>
|
||||
-> Result<(LocalValidationData, GlobalValidationData, ValidationCode), Error>
|
||||
where
|
||||
P: ProvideRuntimeApi<Block>,
|
||||
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
|
||||
@@ -261,7 +261,7 @@ where
|
||||
let local_validation = api.local_validation_data(&relay_parent, para_id)?
|
||||
.ok_or_else(|| Error::InactiveParachain(para_id))?;
|
||||
|
||||
let global_validation = api.global_validation_schedule(&relay_parent)?;
|
||||
let global_validation = api.global_validation_data(&relay_parent)?;
|
||||
let validation_code = api.parachain_code(&relay_parent, para_id)?
|
||||
.ok_or_else(|| Error::InactiveParachain(para_id))?;
|
||||
|
||||
|
||||
@@ -547,7 +547,7 @@ mod tests {
|
||||
use availability_store::ErasureNetworking;
|
||||
use polkadot_primitives::v0::{
|
||||
PoVBlock, AbridgedCandidateReceipt, ErasureChunk, ValidatorIndex,
|
||||
CollationInfo, DutyRoster, GlobalValidationSchedule, LocalValidationData,
|
||||
CollationInfo, DutyRoster, GlobalValidationData, LocalValidationData,
|
||||
Retriable, CollatorId, BlockData, Chain, AvailableData, SigningContext, ValidationCode,
|
||||
};
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
@@ -697,7 +697,7 @@ mod tests {
|
||||
fn validators(&self) -> Vec<ValidatorId> { self.validators.clone() }
|
||||
fn duty_roster(&self) -> DutyRoster { self.duty_roster.clone() }
|
||||
fn active_parachains() -> Vec<(ParaId, Option<(CollatorId, Retriable)>)> { vec![(ParaId::from(1), None)] }
|
||||
fn global_validation_schedule() -> GlobalValidationSchedule { Default::default() }
|
||||
fn global_validation_data() -> GlobalValidationData { Default::default() }
|
||||
fn local_validation_data(_: ParaId) -> Option<LocalValidationData> { None }
|
||||
fn parachain_code(_: ParaId) -> Option<ValidationCode> { None }
|
||||
fn get_heads(_: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<AbridgedCandidateReceipt>> {
|
||||
|
||||
Reference in New Issue
Block a user