From 09f602f8dedbdb47a8a4f198b5ec2f3dc8e5ad52 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 23 Jul 2020 15:02:24 -0400 Subject: [PATCH] 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 --- polkadot/collator/src/lib.rs | 10 +- polkadot/network/src/protocol/tests.rs | 4 +- polkadot/node/core/backing/src/lib.rs | 20 +- polkadot/node/primitives/src/lib.rs | 4 +- .../adder/collator/src/main.rs | 4 +- polkadot/primitives/src/v0.rs | 8 +- polkadot/primitives/src/v1.rs | 61 +++- .../src/runtime-api/README.md | 4 +- .../src/runtime/configuration.md | 3 + .../src/runtime/inclusion.md | 1 + .../implementers-guide/src/runtime/paras.md | 1 + .../src/types/availability.md | 4 +- .../implementers-guide/src/types/candidate.md | 19 +- .../src/types/overseer-protocol.md | 2 +- polkadot/runtime/common/src/parachains.rs | 12 +- polkadot/runtime/common/src/registrar.rs | 2 +- polkadot/runtime/kusama/src/lib.rs | 6 +- .../runtime/parachains/src/configuration.rs | 13 +- polkadot/runtime/parachains/src/inclusion.rs | 316 ++++++++++++++---- polkadot/runtime/parachains/src/paras.rs | 35 +- .../parachains/src/runtime_api_impl/v1.rs | 50 +-- polkadot/runtime/polkadot/src/lib.rs | 6 +- polkadot/runtime/test-runtime/src/lib.rs | 4 +- polkadot/runtime/westend/src/lib.rs | 6 +- polkadot/validation/src/pipeline.rs | 10 +- .../validation/src/validation_service/mod.rs | 4 +- 26 files changed, 434 insertions(+), 175 deletions(-) diff --git a/polkadot/collator/src/lib.rs b/polkadot/collator/src/lib.rs index 2e2b330b0c5..0b7419c29f5 100644 --- a/polkadot/collator/src/lib.rs +++ b/polkadot/collator/src/lib.rs @@ -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 { diff --git a/polkadot/network/src/protocol/tests.rs b/polkadot/network/src/protocol/tests.rs index 711906797be..4e7027963cb 100644 --- a/polkadot/network/src/protocol/tests.rs +++ b/polkadot/network/src/protocol/tests.rs @@ -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() } diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 3d5440968db..4647526fefa 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -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(), diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 6630199ae50..5064a851588 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -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. diff --git a/polkadot/parachain/test-parachains/adder/collator/src/main.rs b/polkadot/parachain/test-parachains/adder/collator/src/main.rs index ec5b626883c..a5d3bb6dc14 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/main.rs @@ -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 diff --git a/polkadot/primitives/src/v0.rs b/polkadot/primitives/src/v0.rs index e1ec3a55463..3fcb31193aa 100644 --- a/polkadot/primitives/src/v0.rs +++ b/polkadot/primitives/src/v0.rs @@ -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. diff --git a/polkadot/primitives/src/v1.rs b/polkadot/primitives/src/v1.rs index 796dd4fb315..5d523fd3007 100644 --- a/polkadot/primitives/src/v1.rs +++ b/polkadot/primitives/src/v1.rs @@ -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]), + ); + } } diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/README.md b/polkadot/roadmap/implementers-guide/src/runtime-api/README.md index 4f36ac5d108..cb8998230d4 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime-api/README.md +++ b/polkadot/roadmap/implementers-guide/src/runtime-api/README.md @@ -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 diff --git a/polkadot/roadmap/implementers-guide/src/runtime/configuration.md b/polkadot/roadmap/implementers-guide/src/runtime/configuration.md index 37e5202429e..f1ed5eb5085 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/configuration.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/configuration.md @@ -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 diff --git a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md index 2387a9244a5..e961b78bed7 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md @@ -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. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/paras.md b/polkadot/roadmap/implementers-guide/src/runtime/paras.md index e80c2d102d2..97c49ae1673 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/paras.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/paras.md @@ -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 diff --git a/polkadot/roadmap/implementers-guide/src/types/availability.md b/polkadot/roadmap/implementers-guide/src/types/availability.md index be42c7a9278..3afa8d8df54 100644 --- a/polkadot/roadmap/implementers-guide/src/types/availability.md +++ b/polkadot/roadmap/implementers-guide/src/types/availability.md @@ -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, } diff --git a/polkadot/roadmap/implementers-guide/src/types/candidate.md b/polkadot/roadmap/implementers-guide/src/types/candidate.md index 0002851cee1..dd69b233806 100644 --- a/polkadot/roadmap/implementers-guide/src/types/candidate.md +++ b/polkadot/roadmap/implementers-guide/src/types/candidate.md @@ -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. diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index f6ff4213fd0..d777460176a 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -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, diff --git a/polkadot/runtime/common/src/parachains.rs b/polkadot/runtime/common/src/parachains.rs index 2df3669ded4..a5d81989aa5 100644 --- a/polkadot/runtime/common/src/parachains.rs +++ b/polkadot/runtime/common/src/parachains.rs @@ -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(), } diff --git a/polkadot/runtime/common/src/registrar.rs b/polkadot/runtime/common/src/registrar.rs index e3b6d5ec12f..5493d20839f 100644 --- a/polkadot/runtime/common/src/registrar.rs +++ b/polkadot/runtime/common/src/registrar.rs @@ -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, diff --git a/polkadot/runtime/kusama/src/lib.rs b/polkadot/runtime/kusama/src/lib.rs index 7354feb6ddd..3e921b433b1 100644 --- a/polkadot/runtime/kusama/src/lib.rs +++ b/polkadot/runtime/kusama/src/lib.rs @@ -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) diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index b977bc119b1..6fc9dd4230b 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -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)] diff --git a/polkadot/runtime/parachains/src/inclusion.rs b/polkadot/runtime/parachains/src/inclusion.rs index 435342cfb82..e6f5ff1a0a5 100644 --- a/polkadot/runtime/parachains/src/inclusion.rs +++ b/polkadot/runtime/parachains/src/inclusion.rs @@ -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); diff --git a/polkadot/runtime/parachains/src/paras.rs b/polkadot/runtime/parachains/src/paras.rs index 0117089f110..2eab9c05465 100644 --- a/polkadot/runtime/parachains/src/paras.rs +++ b/polkadot/runtime/parachains/src/paras.rs @@ -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)] diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs index fa5026dbada..7f4a9093d42 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v1.rs @@ -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) } } } diff --git a/polkadot/runtime/polkadot/src/lib.rs b/polkadot/runtime/polkadot/src/lib.rs index e7afcfe16b7..f8d8cea3ab1 100644 --- a/polkadot/runtime/polkadot/src/lib.rs +++ b/polkadot/runtime/polkadot/src/lib.rs @@ -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) diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 032efaacfbe..65c92aae0c3 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -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) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 6d7fdc79f00..f48ee40e53e 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -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) diff --git a/polkadot/validation/src/pipeline.rs b/polkadot/validation/src/pipeline.rs index f2a705ba101..663321480c5 100644 --- a/polkadot/validation/src/pipeline.rs +++ b/polkadot/validation/src/pipeline.rs @@ -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))?; diff --git a/polkadot/validation/src/validation_service/mod.rs b/polkadot/validation/src/validation_service/mod.rs index 7f332f4c0d1..11116fcaffd 100644 --- a/polkadot/validation/src/validation_service/mod.rs +++ b/polkadot/validation/src/validation_service/mod.rs @@ -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>> { -- GitLab