Implement validation data refactor (#1585)

* update primitives

* correct parent_head field

* make hrmp field pub

* refactor validation data: runtime

* refactor validation data: messages

* add arguments to full_validation_data runtime API

* port runtime API

* mostly port over candidate validation

* remove some parameters from ValidationParams

* guide: update candidate validation

* update candidate outputs

* update ValidationOutputs in primitives

* port over candidate validation

* add a new test for no-transient behavior

* update util runtime API wrappers

* candidate backing

* fix missing imports

* change some fields of validation data around

* runtime API impl

* update candidate validation

* fix backing tests

* grumbles from review

* fix av-store tests

* fix some more crates

* fix provisioner tests

* fix availability distribution tests

* port collation-generation to new validation data

* fix overseer tests

* Update roadmap/implementers-guide/src/node/utility/candidate-validation.md

Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>

Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
This commit is contained in:
Robert Habermeier
2020-08-18 14:41:40 +02:00
committed by GitHub
parent 3395044402
commit 262574fc49
36 changed files with 619 additions and 1153 deletions
-14
View File
@@ -8694,19 +8694,6 @@ dependencies = [
"test-parachain-adder",
]
[[package]]
name = "test-parachain-code-upgrader"
version = "0.7.22"
dependencies = [
"dlmalloc",
"parity-scale-codec",
"polkadot-parachain",
"sp-io",
"sp-std",
"substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.5.0",
]
[[package]]
name = "test-parachain-halt"
version = "0.8.22"
@@ -8722,7 +8709,6 @@ dependencies = [
"polkadot-parachain",
"sp-core",
"test-parachain-adder",
"test-parachain-code-upgrader",
"test-parachain-halt",
"tiny-keccak 1.5.0",
]
-1
View File
@@ -70,7 +70,6 @@ members = [
"parachain/test-parachains",
"parachain/test-parachains/adder",
"parachain/test-parachains/adder/collator",
"parachain/test-parachains/code-upgrader",
]
[badges]
+39 -67
View File
@@ -34,13 +34,13 @@ use polkadot_node_subsystem::{
metrics::{self, prometheus},
};
use polkadot_node_subsystem_util::{
self as util, request_availability_cores_ctx, request_global_validation_data_ctx,
request_local_validation_data_ctx, request_validators_ctx,
self as util, request_availability_cores_ctx, request_full_validation_data_ctx,
request_validators_ctx,
};
use polkadot_primitives::v1::{
collator_signature_payload, validation_data_hash, AvailableData, CandidateCommitments,
CandidateDescriptor, CandidateReceipt, CoreState, GlobalValidationData, Hash,
LocalValidationData, OccupiedCoreAssumption, PoV,
collator_signature_payload, AvailableData, CandidateCommitments,
CandidateDescriptor, CandidateReceipt, CoreState, Hash, OccupiedCoreAssumption,
PersistedValidationData, PoV,
};
use sp_core::crypto::Pair;
use std::sync::Arc;
@@ -198,13 +198,11 @@ async fn handle_new_activations<Context: SubsystemContext>(
for relay_parent in activated.iter().copied() {
// double-future magic happens here: the first layer of requests takes a mutable borrow of the context, and
// returns a receiver. The second layer of requests actually polls those receivers to completion.
let (global_validation_data, availability_cores, validators) = join!(
request_global_validation_data_ctx(relay_parent, ctx).await?,
let (availability_cores, validators) = join!(
request_availability_cores_ctx(relay_parent, ctx).await?,
request_validators_ctx(relay_parent, ctx).await?,
);
let global_validation_data = global_validation_data??;
let availability_cores = availability_cores??;
let n_validators = validators??.len();
@@ -224,9 +222,10 @@ async fn handle_new_activations<Context: SubsystemContext>(
continue;
}
// we get local validation data synchronously for each core instead of within the subtask loop,
// because we have only a single mutable handle to the context, so the work can't really be distributed
let local_validation_data = match request_local_validation_data_ctx(
// we get validation data synchronously for each core instead of
// within the subtask loop, because we have only a single mutable handle to the
// context, so the work can't really be distributed
let validation_data = match request_full_validation_data_ctx(
relay_parent,
scheduled_core.para_id,
assumption,
@@ -235,30 +234,32 @@ async fn handle_new_activations<Context: SubsystemContext>(
.await?
.await??
{
Some(local_validation_data) => local_validation_data,
Some(v) => v,
None => continue,
};
let task_global_validation_data = global_validation_data.clone();
let task_config = config.clone();
let mut task_sender = sender.clone();
let metrics = metrics.clone();
ctx.spawn("collation generation collation builder", Box::pin(async move {
let validation_data_hash =
validation_data_hash(&task_global_validation_data, &local_validation_data);
let persisted_validation_data_hash = validation_data.persisted.hash();
let collation = (task_config.collator)(&task_global_validation_data, &local_validation_data).await;
let collation = (task_config.collator)(&validation_data).await;
let pov_hash = collation.proof_of_validity.hash();
let signature_payload = collator_signature_payload(
&relay_parent,
&scheduled_core.para_id,
&validation_data_hash,
&persisted_validation_data_hash,
&pov_hash,
);
let erasure_root = match erasure_root(n_validators, local_validation_data, task_global_validation_data, collation.proof_of_validity.clone()) {
let erasure_root = match erasure_root(
n_validators,
validation_data.persisted,
collation.proof_of_validity.clone(),
) {
Ok(erasure_root) => erasure_root,
Err(err) => {
log::error!(target: "collation_generation", "failed to calculate erasure root for para_id {}: {:?}", scheduled_core.para_id, err);
@@ -281,7 +282,7 @@ async fn handle_new_activations<Context: SubsystemContext>(
para_id: scheduled_core.para_id,
relay_parent,
collator: task_config.key.public(),
validation_data_hash,
persisted_validation_data_hash,
pov_hash,
},
};
@@ -302,17 +303,11 @@ async fn handle_new_activations<Context: SubsystemContext>(
fn erasure_root(
n_validators: usize,
local_validation_data: LocalValidationData,
global_validation_data: GlobalValidationData,
persisted_validation: PersistedValidationData,
pov: PoV,
) -> Result<Hash> {
let omitted_validation = polkadot_primitives::v1::OmittedValidationData {
global_validation: global_validation_data,
local_validation: local_validation_data,
};
let available_data = AvailableData {
omitted_validation,
validation_data: persisted_validation,
pov,
};
@@ -369,8 +364,8 @@ mod tests {
subsystem_test_harness, TestSubsystemContextHandle,
};
use polkadot_primitives::v1::{
BlockData, BlockNumber, CollatorPair, GlobalValidationData, Id as ParaId,
LocalValidationData, PoV, ScheduledCore,
BlockData, BlockNumber, CollatorPair, Id as ParaId,
PersistedValidationData, PoV, ScheduledCore, ValidationData,
};
use std::pin::Pin;
@@ -402,7 +397,7 @@ mod tests {
fn test_config<Id: Into<ParaId>>(para_id: Id) -> Arc<CollationGenerationConfig> {
Arc::new(CollationGenerationConfig {
key: CollatorPair::generate().0,
collator: Box::new(|_gvd: &GlobalValidationData, _lvd: &LocalValidationData| {
collator: Box::new(|_vd: &ValidationData| {
Box::new(TestCollator)
}),
para_id: para_id.into(),
@@ -417,7 +412,7 @@ mod tests {
}
#[test]
fn requests_validation_and_availability_per_relay_parent() {
fn requests_availability_per_relay_parent() {
let activated_hashes: Vec<Hash> = vec![
[1; 32].into(),
[4; 32].into(),
@@ -425,19 +420,13 @@ mod tests {
[16; 32].into(),
];
let requested_validation_data = Arc::new(Mutex::new(Vec::new()));
let requested_availability_cores = Arc::new(Mutex::new(Vec::new()));
let overseer_requested_validation_data = requested_validation_data.clone();
let overseer_requested_availability_cores = requested_availability_cores.clone();
let overseer = |mut handle: TestSubsystemContextHandle<CollationGenerationMessage>| async move {
loop {
match handle.try_recv().await {
None => break,
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::GlobalValidationData(tx)))) => {
overseer_requested_validation_data.lock().await.push(hash);
tx.send(Ok(Default::default())).unwrap();
}
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::AvailabilityCores(tx)))) => {
overseer_requested_availability_cores.lock().await.push(hash);
tx.send(Ok(vec![])).unwrap();
@@ -455,7 +444,7 @@ mod tests {
let subsystem_activated_hashes = activated_hashes.clone();
subsystem_test_harness(overseer, |mut ctx| async move {
handle_new_activations(
test_config(123),
test_config(123u32),
&subsystem_activated_hashes,
&mut ctx,
Metrics(None),
@@ -465,21 +454,16 @@ mod tests {
.unwrap();
});
let mut requested_validation_data = Arc::try_unwrap(requested_validation_data)
.expect("overseer should have shut down by now")
.into_inner();
requested_validation_data.sort();
let mut requested_availability_cores = Arc::try_unwrap(requested_availability_cores)
.expect("overseer should have shut down by now")
.into_inner();
requested_availability_cores.sort();
assert_eq!(requested_validation_data, activated_hashes);
assert_eq!(requested_availability_cores, activated_hashes);
}
#[test]
fn requests_local_validation_for_scheduled_matches() {
fn requests_validation_data_for_scheduled_matches() {
let activated_hashes: Vec<Hash> = vec![
Hash::repeat_byte(1),
Hash::repeat_byte(4),
@@ -487,19 +471,13 @@ mod tests {
Hash::repeat_byte(16),
];
let requested_local_validation_data = Arc::new(Mutex::new(Vec::new()));
let requested_full_validation_data = Arc::new(Mutex::new(Vec::new()));
let overseer_requested_local_validation_data = requested_local_validation_data.clone();
let overseer_requested_full_validation_data = requested_full_validation_data.clone();
let overseer = |mut handle: TestSubsystemContextHandle<CollationGenerationMessage>| async move {
loop {
match handle.try_recv().await {
None => break,
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::GlobalValidationData(tx),
))) => {
tx.send(Ok(Default::default())).unwrap();
}
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::AvailabilityCores(tx),
@@ -518,13 +496,13 @@ mod tests {
}
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::LocalValidationData(
RuntimeApiRequest::FullValidationData(
_para_id,
_occupied_core_assumption,
tx,
),
))) => {
overseer_requested_local_validation_data
overseer_requested_full_validation_data
.lock()
.await
.push(hash);
@@ -551,7 +529,7 @@ mod tests {
.unwrap();
});
let requested_local_validation_data = Arc::try_unwrap(requested_local_validation_data)
let requested_full_validation_data = Arc::try_unwrap(requested_full_validation_data)
.expect("overseer should have shut down by now")
.into_inner();
@@ -559,7 +537,7 @@ mod tests {
// each activated hash generates two scheduled cores: one with its value * 4, one with its value * 5
// given that the test configuration has a para_id of 16, there's only one way to get that value: with the 4
// hash.
assert_eq!(requested_local_validation_data, vec![[4; 32].into()]);
assert_eq!(requested_full_validation_data, vec![[4; 32].into()]);
}
#[test]
@@ -575,12 +553,6 @@ mod tests {
loop {
match handle.try_recv().await {
None => break,
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::GlobalValidationData(tx),
))) => {
tx.send(Ok(Default::default())).unwrap();
}
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::AvailabilityCores(tx),
@@ -599,7 +571,7 @@ mod tests {
}
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::LocalValidationData(
RuntimeApiRequest::FullValidationData(
_para_id,
_occupied_core_assumption,
tx,
@@ -647,8 +619,8 @@ mod tests {
// we don't care too much about the commitments_hash right now, but let's ensure that we've calculated the
// correct descriptor
let expect_pov_hash = test_collation().proof_of_validity.hash();
let expect_validation_data_hash =
validation_data_hash::<BlockNumber>(&Default::default(), &Default::default());
let expect_validation_data_hash
= PersistedValidationData::<BlockNumber>::default().hash();
let expect_relay_parent = Hash::repeat_byte(4);
let expect_payload = collator_signature_payload(
&expect_relay_parent,
@@ -661,7 +633,7 @@ mod tests {
para_id: config.para_id,
relay_parent: expect_relay_parent,
collator: config.key.public(),
validation_data_hash: expect_validation_data_hash,
persisted_validation_data_hash: expect_validation_data_hash,
pov_hash: expect_pov_hash,
};
@@ -680,7 +652,7 @@ mod tests {
&collator_signature_payload(
&descriptor.relay_parent,
&descriptor.para_id,
&descriptor.validation_data_hash,
&descriptor.persisted_validation_data_hash,
&descriptor.pov_hash,
)
.as_ref(),
+7 -34
View File
@@ -356,8 +356,7 @@ mod tests {
};
use std::cell::RefCell;
use polkadot_primitives::v1::{
AvailableData, BlockData, HeadData, GlobalValidationData, LocalValidationData, PoV,
OmittedValidationData,
AvailableData, BlockData, HeadData, PersistedValidationData, PoV,
};
use polkadot_node_subsystem_test_helpers as test_helpers;
@@ -370,29 +369,19 @@ mod tests {
}
struct TestState {
global_validation_schedule: GlobalValidationData,
local_validation_data: LocalValidationData,
persisted_validation_data: PersistedValidationData,
}
impl Default for TestState {
fn default() -> Self {
let local_validation_data = LocalValidationData {
let persisted_validation_data = PersistedValidationData {
parent_head: HeadData(vec![7, 8, 9]),
balance: Default::default(),
code_upgrade_allowed: None,
validation_code_hash: Default::default(),
};
let global_validation_schedule = GlobalValidationData {
max_code_size: 1000,
max_head_data_size: 1000,
block_number: Default::default(),
hrmp_mqc_heads: Vec::new(),
};
Self {
local_validation_data,
global_validation_schedule,
persisted_validation_data,
}
}
}
@@ -470,17 +459,9 @@ mod tests {
block_data: BlockData(vec![4, 5, 6]),
};
let global_validation = test_state.global_validation_schedule;
let local_validation = test_state.local_validation_data;
let omitted_validation = OmittedValidationData {
global_validation,
local_validation,
};
let available_data = AvailableData {
pov,
omitted_validation,
validation_data: test_state.persisted_validation_data,
};
@@ -531,17 +512,9 @@ mod tests {
block_data: BlockData(vec![4, 5, 6]),
};
let global_validation = test_state.global_validation_schedule;
let local_validation = test_state.local_validation_data;
let omitted_validation = OmittedValidationData {
global_validation,
local_validation,
};
let available_data = AvailableData {
pov,
omitted_validation,
validation_data: test_state.persisted_validation_data,
};
let no_metrics = Metrics(None);
+22 -37
View File
@@ -30,7 +30,7 @@ use futures::{
use keystore::KeyStorePtr;
use polkadot_primitives::v1::{
CommittedCandidateReceipt, BackedCandidate, Id as ParaId, ValidatorId,
ValidatorIndex, SigningContext, PoV, OmittedValidationData,
ValidatorIndex, SigningContext, PoV,
CandidateDescriptor, AvailableData, ValidatorSignature, Hash, CandidateReceipt,
CandidateCommitments, CoreState, CoreIndex,
};
@@ -623,14 +623,9 @@ impl CandidateBackingJob {
outputs: ValidationOutputs,
with_commitments: impl FnOnce(CandidateCommitments) -> Result<T, E>,
) -> Result<Result<T, E>, Error> {
let omitted_validation = OmittedValidationData {
global_validation: outputs.global_validation_data,
local_validation: outputs.local_validation_data,
};
let available_data = AvailableData {
pov,
omitted_validation,
validation_data: outputs.validation_data,
};
let chunks = erasure_coding::obtain_chunks_v1(
@@ -835,7 +830,7 @@ mod tests {
use futures::{executor, future, Future};
use polkadot_primitives::v1::{
ScheduledCore, BlockData, CandidateCommitments, CollatorId,
LocalValidationData, GlobalValidationData, HeadData,
PersistedValidationData, ValidationData, TransientValidationData, HeadData,
ValidatorPair, ValidityAttestation, GroupRotationInfo,
};
use polkadot_subsystem::{
@@ -855,8 +850,7 @@ mod tests {
keystore: KeyStorePtr,
validators: Vec<Sr25519Keyring>,
validator_public: Vec<ValidatorId>,
global_validation_data: GlobalValidationData,
local_validation_data: LocalValidationData,
validation_data: ValidationData,
validator_groups: (Vec<Vec<ValidatorIndex>>, GroupRotationInfo),
availability_cores: Vec<CoreState>,
head_data: HashMap<ParaId, HeadData>,
@@ -920,17 +914,18 @@ mod tests {
parent_hash: relay_parent,
};
let local_validation_data = LocalValidationData {
parent_head: HeadData(vec![7, 8, 9]),
balance: Default::default(),
code_upgrade_allowed: None,
validation_code_hash: Default::default(),
};
let global_validation_data = GlobalValidationData {
max_code_size: 1000,
max_head_data_size: 1000,
block_number: Default::default(),
let validation_data = ValidationData {
persisted: PersistedValidationData {
parent_head: HeadData(vec![7, 8, 9]),
block_number: Default::default(),
hrmp_mqc_heads: Vec::new(),
},
transient: TransientValidationData {
max_code_size: 1000,
max_head_data_size: 1000,
balance: Default::default(),
code_upgrade_allowed: None,
},
};
Self {
@@ -941,8 +936,7 @@ mod tests {
validator_groups: (validator_groups, group_rotation_info),
availability_cores,
head_data,
local_validation_data,
global_validation_data,
validation_data,
signing_context,
relay_parent,
}
@@ -971,13 +965,8 @@ mod tests {
}
fn make_erasure_root(test: &TestState, pov: PoV) -> Hash {
let omitted_validation = OmittedValidationData {
global_validation: test.global_validation_data.clone(),
local_validation: test.local_validation_data.clone(),
};
let available_data = AvailableData {
omitted_validation,
validation_data: test.validation_data.persisted.clone(),
pov,
};
@@ -1109,8 +1098,7 @@ mod tests {
) if pov == pov && &c == candidate.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
validation_data: test_state.validation_data.persisted,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
fees: Default::default(),
@@ -1221,8 +1209,7 @@ mod tests {
) if pov == pov && &c == candidate_a.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
validation_data: test_state.validation_data.persisted,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
fees: Default::default(),
@@ -1351,8 +1338,7 @@ mod tests {
) if pov == pov && &c == candidate_a.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
validation_data: test_state.validation_data.persisted,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
fees: Default::default(),
@@ -1508,8 +1494,7 @@ mod tests {
) if pov == pov && &c == candidate_b.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
validation_data: test_state.validation_data.persisted,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
fees: Default::default(),
+140 -152
View File
@@ -32,8 +32,8 @@ use polkadot_subsystem::{
use polkadot_subsystem::errors::RuntimeApiError;
use polkadot_node_primitives::{ValidationResult, ValidationOutputs, InvalidCandidate};
use polkadot_primitives::v1::{
ValidationCode, OmittedValidationData, PoV, CandidateDescriptor, LocalValidationData,
GlobalValidationData, OccupiedCoreAssumption, Hash, validation_data_hash,
ValidationCode, PoV, CandidateDescriptor, ValidationData, PersistedValidationData,
TransientValidationData, OccupiedCoreAssumption, Hash,
};
use polkadot_parachain::wasm_executor::{self, ValidationPool, ExecutionMode, ValidationError,
InvalidCandidate as WasmInvalidCandidate};
@@ -158,7 +158,8 @@ async fn run(
}
}
CandidateValidationMessage::ValidateFromExhaustive(
omitted_validation,
persisted_validation_data,
transient_validation_data,
validation_code,
descriptor,
pov,
@@ -167,7 +168,8 @@ async fn run(
let res = spawn_validate_exhaustive(
&mut ctx,
Some(pool.clone()),
omitted_validation,
persisted_validation_data,
transient_validation_data,
validation_code,
descriptor,
pov,
@@ -210,7 +212,7 @@ async fn runtime_api_request<T>(
#[derive(Debug)]
enum AssumptionCheckOutcome {
Matches(OmittedValidationData, ValidationCode),
Matches(ValidationData, ValidationCode),
DoesNotMatch,
BadRequest,
}
@@ -218,15 +220,14 @@ enum AssumptionCheckOutcome {
async fn check_assumption_validation_data(
ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
descriptor: &CandidateDescriptor,
global_validation_data: &GlobalValidationData,
assumption: OccupiedCoreAssumption,
) -> SubsystemResult<AssumptionCheckOutcome> {
let local_validation_data = {
let validation_data = {
let (tx, rx) = oneshot::channel();
let d = runtime_api_request(
ctx,
descriptor.relay_parent,
RuntimeApiRequest::LocalValidationData(
RuntimeApiRequest::FullValidationData(
descriptor.para_id,
assumption,
tx,
@@ -242,17 +243,9 @@ async fn check_assumption_validation_data(
}
};
let validation_data_hash = validation_data_hash(
&global_validation_data,
&local_validation_data,
);
SubsystemResult::Ok(if descriptor.validation_data_hash == validation_data_hash {
let omitted_validation = OmittedValidationData {
global_validation: global_validation_data.clone(),
local_validation: local_validation_data,
};
let persisted_validation_data_hash = validation_data.persisted.hash();
SubsystemResult::Ok(if descriptor.persisted_validation_data_hash == persisted_validation_data_hash {
let (code_tx, code_rx) = oneshot::channel();
let validation_code = runtime_api_request(
ctx,
@@ -267,7 +260,7 @@ async fn check_assumption_validation_data(
match validation_code {
Ok(None) | Err(_) => AssumptionCheckOutcome::BadRequest,
Ok(Some(v)) => AssumptionCheckOutcome::Matches(omitted_validation, v),
Ok(Some(v)) => AssumptionCheckOutcome::Matches(validation_data, v),
}
} else {
AssumptionCheckOutcome::DoesNotMatch
@@ -281,44 +274,21 @@ async fn spawn_validate_from_chain_state(
pov: Arc<PoV>,
spawn: impl SpawnNamed + 'static,
) -> SubsystemResult<Result<ValidationResult, ValidationFailed>> {
// The candidate descriptor has a `validation_data_hash` which corresponds to
// The candidate descriptor has a `persisted_validation_data_hash` which corresponds to
// one of up to two possible values that we can derive from the state of the
// relay-parent. We can fetch these values by getting the `global_validation_data`,
// and both `local_validation_data` based on the different `OccupiedCoreAssumption`s.
let global_validation_data = {
let (tx, rx) = oneshot::channel();
let res = runtime_api_request(
ctx,
descriptor.relay_parent,
RuntimeApiRequest::GlobalValidationData(tx),
rx,
).await?;
match res {
Ok(g) => g,
Err(e) => {
log::warn!(
target: LOG_TARGET,
"Error making runtime API request: {:?}",
e,
);
return Ok(Err(ValidationFailed("Error making API request".into())));
}
}
};
// relay-parent. We can fetch these values by getting the persisted validation data
// based on the different `OccupiedCoreAssumption`s.
match check_assumption_validation_data(
ctx,
&descriptor,
&global_validation_data,
OccupiedCoreAssumption::Included,
).await? {
AssumptionCheckOutcome::Matches(omitted_validation, validation_code) => {
AssumptionCheckOutcome::Matches(validation_data, validation_code) => {
return spawn_validate_exhaustive(
ctx,
validation_pool,
omitted_validation,
validation_data.persisted,
Some(validation_data.transient),
validation_code,
descriptor,
pov,
@@ -332,14 +302,14 @@ async fn spawn_validate_from_chain_state(
match check_assumption_validation_data(
ctx,
&descriptor,
&global_validation_data,
OccupiedCoreAssumption::TimedOut,
).await? {
AssumptionCheckOutcome::Matches(omitted_validation, validation_code) => {
AssumptionCheckOutcome::Matches(validation_data, validation_code) => {
return spawn_validate_exhaustive(
ctx,
validation_pool,
omitted_validation,
validation_data.persisted,
Some(validation_data.transient),
validation_code,
descriptor,
pov,
@@ -351,7 +321,7 @@ async fn spawn_validate_from_chain_state(
}
// If neither the assumption of the occupied core having the para included or the assumption
// of the occupied core timing out are valid, then the validation_data_hash in the descriptor
// of the occupied core timing out are valid, then the persisted_validation_data_hash in the descriptor
// is not based on the relay parent and is thus invalid.
Ok(Ok(ValidationResult::Invalid(InvalidCandidate::BadParent)))
}
@@ -359,7 +329,8 @@ async fn spawn_validate_from_chain_state(
async fn spawn_validate_exhaustive(
ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
validation_pool: Option<ValidationPool>,
omitted_validation: OmittedValidationData,
persisted_validation_data: PersistedValidationData,
transient_validation_data: Option<TransientValidationData>,
validation_code: ValidationCode,
descriptor: CandidateDescriptor,
pov: Arc<PoV>,
@@ -369,7 +340,8 @@ async fn spawn_validate_exhaustive(
let fut = async move {
let res = validate_candidate_exhaustive::<RealValidationBackend, _>(
validation_pool,
omitted_validation,
persisted_validation_data,
transient_validation_data,
validation_code,
descriptor,
pov,
@@ -414,16 +386,19 @@ fn perform_basic_checks(
///
/// Returns `Ok(())` if checks pass, error otherwise.
fn check_wasm_result_against_constraints(
global_validation_data: &GlobalValidationData,
_local_validation_data: &LocalValidationData,
transient_params: &TransientValidationData,
result: &WasmValidationResult,
) -> Result<(), InvalidCandidate> {
if result.head_data.0.len() > global_validation_data.max_head_data_size as _ {
if result.head_data.0.len() > transient_params.max_head_data_size as _ {
return Err(InvalidCandidate::HeadDataTooLarge(result.head_data.0.len() as u64))
}
if let Some(ref code) = result.new_validation_code {
if code.0.len() > global_validation_data.max_code_size as _ {
if transient_params.code_upgrade_allowed.is_none() {
return Err(InvalidCandidate::CodeUpgradeNotAllowed)
}
if code.0.len() > transient_params.max_code_size as _ {
return Err(InvalidCandidate::NewCodeTooLarge(code.0.len() as u64))
}
}
@@ -471,7 +446,8 @@ impl ValidationBackend for RealValidationBackend {
/// Sends the result of validation on the channel once complete.
fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
backend_arg: B::Arg,
omitted_validation: OmittedValidationData,
persisted_validation_data: PersistedValidationData,
transient_validation_data: Option<TransientValidationData>,
validation_code: ValidationCode,
descriptor: CandidateDescriptor,
pov: Arc<PoV>,
@@ -481,15 +457,11 @@ fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
return Ok(ValidationResult::Invalid(e))
}
let OmittedValidationData { global_validation, local_validation } = omitted_validation;
let params = ValidationParams {
parent_head: local_validation.parent_head.clone(),
parent_head: persisted_validation_data.parent_head.clone(),
block_data: pov.block_data.clone(),
max_code_size: global_validation.max_code_size,
max_head_data_size: global_validation.max_head_data_size,
relay_chain_height: global_validation.block_number,
code_upgrade_allowed: local_validation.code_upgrade_allowed,
relay_chain_height: persisted_validation_data.block_number,
hrmp_mqc_heads: persisted_validation_data.hrmp_mqc_heads.clone(),
};
match B::validate(backend_arg, &validation_code, params, spawn) {
@@ -507,17 +479,19 @@ fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e.to_string()))),
Err(ValidationError::Internal(e)) => Err(ValidationFailed(e.to_string())),
Ok(res) => {
let post_check_result = check_wasm_result_against_constraints(
&global_validation,
&local_validation,
&res,
);
let post_check_result = if let Some(transient) = transient_validation_data {
check_wasm_result_against_constraints(
&transient,
&res,
)
} else {
Ok(())
};
Ok(match post_check_result {
Ok(()) => ValidationResult::Valid(ValidationOutputs {
head_data: res.head_data,
global_validation_data: global_validation,
local_validation_data: local_validation,
validation_data: persisted_validation_data,
upward_messages: res.upward_messages,
fees: 0,
new_validation_code: res.new_validation_code,
@@ -562,7 +536,7 @@ mod tests {
let payload = polkadot_primitives::v1::collator_signature_payload(
&descriptor.relay_parent,
&descriptor.para_id,
&descriptor.validation_data_hash,
&descriptor.persisted_validation_data_hash,
&descriptor.pov_hash,
);
@@ -572,17 +546,16 @@ mod tests {
#[test]
fn correctly_checks_included_assumption() {
let local_validation_data = LocalValidationData::default();
let global_validation_data = GlobalValidationData::default();
let validation_data: ValidationData = Default::default();
let validation_code: ValidationCode = vec![1, 2, 3].into();
let validation_data_hash = validation_data_hash(&global_validation_data, &local_validation_data);
let persisted_validation_data_hash = validation_data.persisted.hash();
let relay_parent = [2; 32].into();
let para_id = 5.into();
let mut candidate = CandidateDescriptor::default();
candidate.relay_parent = relay_parent;
candidate.validation_data_hash = validation_data_hash;
candidate.persisted_validation_data_hash = persisted_validation_data_hash;
candidate.para_id = para_id;
let pool = TaskExecutor::new();
@@ -591,22 +564,20 @@ mod tests {
let (check_fut, check_result) = check_assumption_validation_data(
&mut ctx,
&candidate,
&global_validation_data,
OccupiedCoreAssumption::Included,
).remote_handle();
let global_validation_data = global_validation_data.clone();
let test_fut = async move {
assert_matches!(
ctx_handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
rp,
RuntimeApiRequest::LocalValidationData(p, OccupiedCoreAssumption::Included, tx)
RuntimeApiRequest::FullValidationData(p, OccupiedCoreAssumption::Included, tx)
)) => {
assert_eq!(rp, relay_parent);
assert_eq!(p, para_id);
let _ = tx.send(Ok(Some(local_validation_data.clone())));
let _ = tx.send(Ok(Some(validation_data.clone())));
}
);
@@ -624,11 +595,7 @@ mod tests {
);
assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::Matches(o, v) => {
assert_eq!(o, OmittedValidationData {
local_validation: local_validation_data,
global_validation: global_validation_data,
});
assert_eq!(o, validation_data);
assert_eq!(v, validation_code);
});
};
@@ -639,17 +606,16 @@ mod tests {
#[test]
fn correctly_checks_timed_out_assumption() {
let local_validation_data = LocalValidationData::default();
let global_validation_data = GlobalValidationData::default();
let validation_data: ValidationData = Default::default();
let validation_code: ValidationCode = vec![1, 2, 3].into();
let validation_data_hash = validation_data_hash(&global_validation_data, &local_validation_data);
let persisted_validation_data_hash = validation_data.persisted.hash();
let relay_parent = [2; 32].into();
let para_id = 5.into();
let mut candidate = CandidateDescriptor::default();
candidate.relay_parent = relay_parent;
candidate.validation_data_hash = validation_data_hash;
candidate.persisted_validation_data_hash = persisted_validation_data_hash;
candidate.para_id = para_id;
let pool = TaskExecutor::new();
@@ -658,22 +624,20 @@ mod tests {
let (check_fut, check_result) = check_assumption_validation_data(
&mut ctx,
&candidate,
&global_validation_data,
OccupiedCoreAssumption::TimedOut,
).remote_handle();
let global_validation_data = global_validation_data.clone();
let test_fut = async move {
assert_matches!(
ctx_handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
rp,
RuntimeApiRequest::LocalValidationData(p, OccupiedCoreAssumption::TimedOut, tx)
RuntimeApiRequest::FullValidationData(p, OccupiedCoreAssumption::TimedOut, tx)
)) => {
assert_eq!(rp, relay_parent);
assert_eq!(p, para_id);
let _ = tx.send(Ok(Some(local_validation_data.clone())));
let _ = tx.send(Ok(Some(validation_data.clone())));
}
);
@@ -691,11 +655,7 @@ mod tests {
);
assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::Matches(o, v) => {
assert_eq!(o, OmittedValidationData {
local_validation: local_validation_data,
global_validation: global_validation_data,
});
assert_eq!(o, validation_data);
assert_eq!(v, validation_code);
});
};
@@ -706,16 +666,14 @@ mod tests {
#[test]
fn check_is_bad_request_if_no_validation_data() {
let local_validation_data = LocalValidationData::default();
let global_validation_data = GlobalValidationData::default();
let validation_data_hash = validation_data_hash(&global_validation_data, &local_validation_data);
let validation_data: ValidationData = Default::default();
let persisted_validation_data_hash = validation_data.persisted.hash();
let relay_parent = [2; 32].into();
let para_id = 5.into();
let mut candidate = CandidateDescriptor::default();
candidate.relay_parent = relay_parent;
candidate.validation_data_hash = validation_data_hash;
candidate.persisted_validation_data_hash = persisted_validation_data_hash;
candidate.para_id = para_id;
let pool = TaskExecutor::new();
@@ -724,7 +682,6 @@ mod tests {
let (check_fut, check_result) = check_assumption_validation_data(
&mut ctx,
&candidate,
&global_validation_data,
OccupiedCoreAssumption::Included,
).remote_handle();
@@ -733,7 +690,7 @@ mod tests {
ctx_handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
rp,
RuntimeApiRequest::LocalValidationData(p, OccupiedCoreAssumption::Included, tx)
RuntimeApiRequest::FullValidationData(p, OccupiedCoreAssumption::Included, tx)
)) => {
assert_eq!(rp, relay_parent);
assert_eq!(p, para_id);
@@ -751,16 +708,14 @@ mod tests {
#[test]
fn check_is_bad_request_if_no_validation_code() {
let local_validation_data = LocalValidationData::default();
let global_validation_data = GlobalValidationData::default();
let validation_data_hash = validation_data_hash(&global_validation_data, &local_validation_data);
let validation_data: ValidationData = Default::default();
let persisted_validation_data_hash = validation_data.persisted.hash();
let relay_parent = [2; 32].into();
let para_id = 5.into();
let mut candidate = CandidateDescriptor::default();
candidate.relay_parent = relay_parent;
candidate.validation_data_hash = validation_data_hash;
candidate.persisted_validation_data_hash = persisted_validation_data_hash;
candidate.para_id = para_id;
let pool = TaskExecutor::new();
@@ -769,7 +724,6 @@ mod tests {
let (check_fut, check_result) = check_assumption_validation_data(
&mut ctx,
&candidate,
&global_validation_data,
OccupiedCoreAssumption::TimedOut,
).remote_handle();
@@ -778,12 +732,12 @@ mod tests {
ctx_handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
rp,
RuntimeApiRequest::LocalValidationData(p, OccupiedCoreAssumption::TimedOut, tx)
RuntimeApiRequest::FullValidationData(p, OccupiedCoreAssumption::TimedOut, tx)
)) => {
assert_eq!(rp, relay_parent);
assert_eq!(p, para_id);
let _ = tx.send(Ok(Some(local_validation_data.clone())));
let _ = tx.send(Ok(Some(validation_data.clone())));
}
);
@@ -809,15 +763,13 @@ mod tests {
#[test]
fn check_does_not_match() {
let local_validation_data = LocalValidationData::default();
let global_validation_data = GlobalValidationData::default();
let validation_data: ValidationData = Default::default();
let relay_parent = [2; 32].into();
let para_id = 5.into();
let mut candidate = CandidateDescriptor::default();
candidate.relay_parent = relay_parent;
candidate.validation_data_hash = [3; 32].into();
candidate.persisted_validation_data_hash = [3; 32].into();
candidate.para_id = para_id;
let pool = TaskExecutor::new();
@@ -826,7 +778,6 @@ mod tests {
let (check_fut, check_result) = check_assumption_validation_data(
&mut ctx,
&candidate,
&global_validation_data,
OccupiedCoreAssumption::Included,
).remote_handle();
@@ -835,12 +786,12 @@ mod tests {
ctx_handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
rp,
RuntimeApiRequest::LocalValidationData(p, OccupiedCoreAssumption::Included, tx)
RuntimeApiRequest::FullValidationData(p, OccupiedCoreAssumption::Included, tx)
)) => {
assert_eq!(rp, relay_parent);
assert_eq!(p, para_id);
let _ = tx.send(Ok(Some(local_validation_data.clone())));
let _ = tx.send(Ok(Some(validation_data.clone())));
}
);
@@ -853,13 +804,10 @@ mod tests {
#[test]
fn candidate_validation_ok_is_ok() {
let mut omitted_validation = OmittedValidationData {
local_validation: Default::default(),
global_validation: Default::default(),
};
omitted_validation.global_validation.max_head_data_size = 1024;
omitted_validation.global_validation.max_code_size = 1024;
let mut validation_data: ValidationData = Default::default();
validation_data.transient.max_head_data_size = 1024;
validation_data.transient.max_code_size = 1024;
validation_data.transient.code_upgrade_allowed = Some(20);
let pov = PoV { block_data: BlockData(vec![1; 32]) };
@@ -877,14 +825,14 @@ mod tests {
};
assert!(check_wasm_result_against_constraints(
&omitted_validation.global_validation,
&omitted_validation.local_validation,
&validation_data.transient,
&validation_result,
).is_ok());
let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
MockValidationArg { result: Ok(validation_result) },
omitted_validation.clone(),
validation_data.persisted.clone(),
Some(validation_data.transient),
vec![1, 2, 3].into(),
descriptor,
Arc::new(pov),
@@ -893,8 +841,7 @@ mod tests {
assert_matches!(v, ValidationResult::Valid(outputs) => {
assert_eq!(outputs.head_data, HeadData(vec![1, 1, 1]));
assert_eq!(outputs.global_validation_data, omitted_validation.global_validation);
assert_eq!(outputs.local_validation_data, omitted_validation.local_validation);
assert_eq!(outputs.validation_data, validation_data.persisted);
assert_eq!(outputs.upward_messages, Vec::new());
assert_eq!(outputs.fees, 0);
assert_eq!(outputs.new_validation_code, Some(vec![2, 2, 2].into()));
@@ -903,13 +850,11 @@ mod tests {
#[test]
fn candidate_validation_bad_return_is_invalid() {
let mut omitted_validation = OmittedValidationData {
local_validation: Default::default(),
global_validation: Default::default(),
};
let mut validation_data: ValidationData = Default::default();
omitted_validation.global_validation.max_head_data_size = 1024;
omitted_validation.global_validation.max_code_size = 1024;
validation_data.transient.max_head_data_size = 1024;
validation_data.transient.max_code_size = 1024;
validation_data.transient.code_upgrade_allowed = Some(20);
let pov = PoV { block_data: BlockData(vec![1; 32]) };
@@ -927,8 +872,7 @@ mod tests {
};
assert!(check_wasm_result_against_constraints(
&omitted_validation.global_validation,
&omitted_validation.local_validation,
&validation_data.transient,
&validation_result,
).is_ok());
@@ -938,7 +882,8 @@ mod tests {
WasmInvalidCandidate::BadReturn
))
},
omitted_validation.clone(),
validation_data.persisted,
Some(validation_data.transient),
vec![1, 2, 3].into(),
descriptor,
Arc::new(pov),
@@ -951,13 +896,11 @@ mod tests {
#[test]
fn candidate_validation_timeout_is_internal_error() {
let mut omitted_validation = OmittedValidationData {
local_validation: Default::default(),
global_validation: Default::default(),
};
let mut validation_data: ValidationData = Default::default();
omitted_validation.global_validation.max_head_data_size = 1024;
omitted_validation.global_validation.max_code_size = 1024;
validation_data.transient.max_head_data_size = 1024;
validation_data.transient.max_code_size = 1024;
validation_data.transient.code_upgrade_allowed = Some(20);
let pov = PoV { block_data: BlockData(vec![1; 32]) };
@@ -975,8 +918,7 @@ mod tests {
};
assert!(check_wasm_result_against_constraints(
&omitted_validation.global_validation,
&omitted_validation.local_validation,
&validation_data.transient,
&validation_result,
).is_ok());
@@ -986,7 +928,8 @@ mod tests {
WasmInvalidCandidate::Timeout
))
},
omitted_validation.clone(),
validation_data.persisted,
Some(validation_data.transient),
vec![1, 2, 3].into(),
descriptor,
Arc::new(pov),
@@ -995,4 +938,49 @@ mod tests {
assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)));
}
#[test]
fn candidate_validation_ok_does_not_validate_outputs_if_no_transient() {
let mut validation_data: ValidationData = Default::default();
validation_data.transient.max_head_data_size = 1;
validation_data.transient.max_code_size = 1;
let pov = PoV { block_data: BlockData(vec![1; 32]) };
let mut descriptor = CandidateDescriptor::default();
descriptor.pov_hash = pov.hash();
collator_sign(&mut descriptor, Sr25519Keyring::Alice);
assert!(perform_basic_checks(&descriptor, Some(1024), &pov).is_ok());
let validation_result = WasmValidationResult {
head_data: HeadData(vec![1, 1, 1]),
new_validation_code: Some(vec![2, 2, 2].into()),
upward_messages: Vec::new(),
processed_downward_messages: 0,
};
assert!(check_wasm_result_against_constraints(
&validation_data.transient,
&validation_result,
).is_err());
let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
MockValidationArg { result: Ok(validation_result) },
validation_data.persisted.clone(),
None,
vec![1, 2, 3].into(),
descriptor,
Arc::new(pov),
TaskExecutor::new(),
).unwrap();
assert_matches!(v, ValidationResult::Valid(outputs) => {
assert_eq!(outputs.head_data, HeadData(vec![1, 1, 1]));
assert_eq!(outputs.validation_data, validation_data.persisted);
assert_eq!(outputs.upward_messages, Vec::new());
assert_eq!(outputs.fees, 0);
assert_eq!(outputs.new_validation_code, Some(vec![2, 2, 2].into()));
});
}
}
+13 -23
View File
@@ -35,11 +35,10 @@ use polkadot_node_subsystem::{
use polkadot_node_subsystem_util::{
self as util,
delegated_subsystem,
request_availability_cores, request_global_validation_data,
request_local_validation_data, JobTrait, ToJobTrait,
request_availability_cores, request_persisted_validation_data, JobTrait, ToJobTrait,
};
use polkadot_primitives::v1::{
validation_data_hash, BackedCandidate, BlockNumber, CoreState, Hash, OccupiedCoreAssumption,
BackedCandidate, BlockNumber, CoreState, Hash, OccupiedCoreAssumption,
SignedAvailabilityBitfield,
};
use std::{collections::HashMap, convert::TryFrom, pin::Pin};
@@ -355,10 +354,6 @@ async fn select_candidates(
) -> Result<Vec<BackedCandidate>, Error> {
let block_number = get_block_number_under_construction(relay_parent, sender).await?;
let global_validation_data = request_global_validation_data(relay_parent, sender)
.await?
.await??;
let mut selected_candidates =
Vec::with_capacity(candidates.len().min(availability_cores.len()));
@@ -387,7 +382,7 @@ async fn select_candidates(
_ => continue,
};
let local_validation_data = match request_local_validation_data(
let validation_data = match request_persisted_validation_data(
relay_parent,
scheduled_core.para_id,
assumption,
@@ -396,18 +391,17 @@ async fn select_candidates(
.await?
.await??
{
Some(local_validation_data) => local_validation_data,
Some(v) => v,
None => continue,
};
let computed_validation_data_hash =
validation_data_hash(&global_validation_data, &local_validation_data);
let computed_validation_data_hash = validation_data.hash();
// we arbitrarily pick the first of the backed candidates which match the appropriate selection criteria
if let Some(candidate) = candidates.iter().find(|backed_candidate| {
let descriptor = &backed_candidate.candidate.descriptor;
descriptor.para_id == scheduled_core.para_id
&& descriptor.validation_data_hash == computed_validation_data_hash
&& descriptor.persisted_validation_data_hash == computed_validation_data_hash
}) {
selected_candidates.push(candidate.clone());
}
@@ -657,10 +651,10 @@ mod tests {
use super::super::*;
use super::{build_occupied_core, default_bitvec, occupied_core, scheduled_core};
use polkadot_node_subsystem::messages::RuntimeApiRequest::{
AvailabilityCores, GlobalValidationData, LocalValidationData,
AvailabilityCores, PersistedValidationData as PersistedValidationDataReq,
};
use polkadot_primitives::v1::{
BlockNumber, CandidateDescriptor, CommittedCandidateReceipt,
BlockNumber, CandidateDescriptor, CommittedCandidateReceipt, PersistedValidationData,
};
use FromJob::{ChainApi, Runtime};
@@ -771,12 +765,9 @@ mod tests {
ChainApi(BlockNumber(_relay_parent, tx)) => {
tx.send(Ok(Some(BLOCK_UNDER_PRODUCTION - 1))).unwrap()
}
Runtime(Request(_parent_hash, GlobalValidationData(tx))) => {
tx.send(Ok(Default::default())).unwrap()
}
Runtime(Request(
_parent_hash,
LocalValidationData(_para_id, _assumption, tx),
PersistedValidationDataReq(_para_id, _assumption, tx),
)) => tx.send(Ok(Some(Default::default()))).unwrap(),
Runtime(Request(_parent_hash, AvailabilityCores(tx))) => {
tx.send(Ok(mock_availability_cores())).unwrap()
@@ -823,14 +814,12 @@ mod tests {
fn selects_correct_candidates() {
let mock_cores = mock_availability_cores();
let empty_hash =
validation_data_hash::<BlockNumber>(&Default::default(), &Default::default());
dbg!(empty_hash);
let empty_hash = PersistedValidationData::<BlockNumber>::default().hash();
let candidate_template = BackedCandidate {
candidate: CommittedCandidateReceipt {
descriptor: CandidateDescriptor {
validation_data_hash: empty_hash,
persisted_validation_data_hash: empty_hash,
..Default::default()
},
..Default::default()
@@ -855,7 +844,8 @@ mod tests {
candidate
} else if idx < mock_cores.len() * 2 {
// for the second repetition of the candidates, give them the wrong hash
candidate.candidate.descriptor.validation_data_hash = Default::default();
candidate.candidate.descriptor.persisted_validation_data_hash
= Default::default();
candidate
} else {
// third go-around: right hash, wrong para_id
+62 -41
View File
@@ -110,9 +110,10 @@ fn make_runtime_api_request<Client>(
Request::Validators(sender) => query!(validators(), sender),
Request::ValidatorGroups(sender) => query!(validator_groups(), sender),
Request::AvailabilityCores(sender) => query!(availability_cores(), sender),
Request::GlobalValidationData(sender) => query!(global_validation_data(), sender),
Request::LocalValidationData(para, assumption, sender) =>
query!(local_validation_data(para, assumption), sender),
Request::PersistedValidationData(para, assumption, sender) =>
query!(persisted_validation_data(para, assumption), sender),
Request::FullValidationData(para, assumption, sender) =>
query!(full_validation_data(para, assumption), sender),
Request::SessionIndexForChild(sender) => query!(session_index_for_child(), sender),
Request::ValidationCode(para, assumption, sender) =>
query!(validation_code(para, assumption), sender),
@@ -166,8 +167,8 @@ mod tests {
use super::*;
use polkadot_primitives::v1::{
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, GlobalValidationData,
Id as ParaId, OccupiedCoreAssumption, LocalValidationData, SessionIndex, ValidationCode,
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, PersistedValidationData,
Id as ParaId, OccupiedCoreAssumption, ValidationData, SessionIndex, ValidationCode,
CommittedCandidateReceipt, CandidateEvent,
};
use polkadot_node_subsystem_test_helpers as test_helpers;
@@ -181,8 +182,7 @@ mod tests {
validators: Vec<ValidatorId>,
validator_groups: Vec<Vec<ValidatorIndex>>,
availability_cores: Vec<CoreState>,
global_validation_data: GlobalValidationData,
local_validation_data: HashMap<ParaId, LocalValidationData>,
validation_data: HashMap<ParaId, ValidationData>,
session_index_for_child: SessionIndex,
validation_code: HashMap<ParaId, ValidationCode>,
candidate_pending_availability: HashMap<ParaId, CommittedCandidateReceipt>,
@@ -220,16 +220,20 @@ mod tests {
self.availability_cores.clone()
}
fn global_validation_data(&self) -> GlobalValidationData {
self.global_validation_data.clone()
}
fn local_validation_data(
fn persisted_validation_data(
&self,
para: ParaId,
_assumption: OccupiedCoreAssumption,
) -> Option<LocalValidationData> {
self.local_validation_data.get(&para).map(|l| l.clone())
) -> Option<PersistedValidationData> {
self.validation_data.get(&para).map(|l| l.persisted.clone())
}
fn full_validation_data(
&self,
para: ParaId,
_assumption: OccupiedCoreAssumption,
) -> Option<ValidationData> {
self.validation_data.get(&para).map(|l| l.clone())
}
fn session_index_for_child(&self) -> SessionIndex {
@@ -327,37 +331,14 @@ mod tests {
}
#[test]
fn requests_global_validation_data() {
let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new());
let runtime_api = MockRuntimeApi::default();
let relay_parent = [1; 32].into();
let subsystem = RuntimeApiSubsystem::new(runtime_api.clone(), Metrics(None));
let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap());
let test_task = async move {
let (tx, rx) = oneshot::channel();
ctx_handle.send(FromOverseer::Communication {
msg: RuntimeApiMessage::Request(relay_parent, Request::GlobalValidationData(tx))
}).await;
assert_eq!(rx.await.unwrap().unwrap(), runtime_api.global_validation_data);
ctx_handle.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
};
futures::executor::block_on(future::join(subsystem_task, test_task));
}
#[test]
fn requests_local_validation_data() {
fn requests_persisted_validation_data() {
let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new());
let mut runtime_api = MockRuntimeApi::default();
let relay_parent = [1; 32].into();
let para_a = 5.into();
let para_b = 6.into();
runtime_api.local_validation_data.insert(para_a, Default::default());
runtime_api.validation_data.insert(para_a, Default::default());
let subsystem = RuntimeApiSubsystem::new(runtime_api.clone(), Metrics(None));
let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap());
@@ -367,7 +348,7 @@ mod tests {
ctx_handle.send(FromOverseer::Communication {
msg: RuntimeApiMessage::Request(
relay_parent,
Request::LocalValidationData(para_a, OccupiedCoreAssumption::Included, tx)
Request::PersistedValidationData(para_a, OccupiedCoreAssumption::Included, tx)
),
}).await;
@@ -377,7 +358,47 @@ mod tests {
ctx_handle.send(FromOverseer::Communication {
msg: RuntimeApiMessage::Request(
relay_parent,
Request::LocalValidationData(para_b, OccupiedCoreAssumption::Included, tx)
Request::PersistedValidationData(para_b, OccupiedCoreAssumption::Included, tx)
),
}).await;
assert_eq!(rx.await.unwrap().unwrap(), None);
ctx_handle.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
};
futures::executor::block_on(future::join(subsystem_task, test_task));
}
#[test]
fn requests_full_validation_data() {
let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new());
let mut runtime_api = MockRuntimeApi::default();
let relay_parent = [1; 32].into();
let para_a = 5.into();
let para_b = 6.into();
runtime_api.validation_data.insert(para_a, Default::default());
let subsystem = RuntimeApiSubsystem::new(runtime_api.clone(), Metrics(None));
let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap());
let test_task = async move {
let (tx, rx) = oneshot::channel();
ctx_handle.send(FromOverseer::Communication {
msg: RuntimeApiMessage::Request(
relay_parent,
Request::FullValidationData(para_a, OccupiedCoreAssumption::Included, tx)
),
}).await;
assert_eq!(rx.await.unwrap().unwrap(), Some(Default::default()));
let (tx, rx) = oneshot::channel();
ctx_handle.send(FromOverseer::Communication {
msg: RuntimeApiMessage::Request(
relay_parent,
Request::FullValidationData(para_b, OccupiedCoreAssumption::Included, tx)
),
}).await;
@@ -18,9 +18,9 @@ use super::*;
use assert_matches::assert_matches;
use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks};
use polkadot_primitives::v1::{
AvailableData, BlockData, CandidateCommitments, CandidateDescriptor, GlobalValidationData,
GroupIndex, GroupRotationInfo, HeadData, LocalValidationData, OccupiedCore,
OmittedValidationData, PoV, ScheduledCore, ValidatorPair,
AvailableData, BlockData, CandidateCommitments, CandidateDescriptor, GroupIndex,
GroupRotationInfo, HeadData, PersistedValidationData, OccupiedCore,
PoV, ScheduledCore, ValidatorPair,
};
use polkadot_subsystem_testhelpers as test_helpers;
use polkadot_node_network_protocol::ObservedRole;
@@ -148,8 +148,7 @@ struct TestState {
relay_parent: Hash,
ancestors: Vec<Hash>,
availability_cores: Vec<CoreState>,
global_validation_data: GlobalValidationData,
local_validation_data: LocalValidationData,
persisted_validation_data: PersistedValidationData,
}
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
@@ -210,17 +209,10 @@ impl Default for TestState {
];
let relay_parent = Hash::repeat_byte(0x05);
let local_validation_data = LocalValidationData {
let persisted_validation_data = PersistedValidationData {
parent_head: HeadData(vec![7, 8, 9]),
balance: Default::default(),
code_upgrade_allowed: None,
validation_code_hash: Default::default(),
};
let global_validation_data = GlobalValidationData {
max_code_size: 1000,
max_head_data_size: 1000,
block_number: Default::default(),
hrmp_mqc_heads: Vec::new(),
};
let validator_index = Some((validators.len() - 1) as ValidatorIndex);
@@ -233,8 +225,7 @@ impl Default for TestState {
validator_groups,
availability_cores,
head_data,
local_validation_data,
global_validation_data,
persisted_validation_data,
relay_parent,
ancestors,
validator_index,
@@ -243,13 +234,8 @@ impl Default for TestState {
}
fn make_available_data(test: &TestState, pov: PoV) -> AvailableData {
let omitted_validation = OmittedValidationData {
global_validation: test.global_validation_data.clone(),
local_validation: test.local_validation_data.clone(),
};
AvailableData {
omitted_validation,
validation_data: test.persisted_validation_data.clone(),
pov,
}
}
@@ -434,8 +420,7 @@ fn reputation_verification() {
validator_groups,
availability_cores,
head_data: _,
local_validation_data: _,
global_validation_data: _,
persisted_validation_data: _,
relay_parent: current,
ancestors,
validator_index: _,
+1 -1
View File
@@ -1800,7 +1800,7 @@ mod tests {
fn test_collator_generation_msg() -> CollationGenerationMessage {
CollationGenerationMessage::Initialize(CollationGenerationConfig {
key: CollatorPair::generate().0,
collator: Box::new(|_, _| Box::new(TestCollator)),
collator: Box::new(|_| Box::new(TestCollator)),
para_id: Default::default(),
})
}
+6 -6
View File
@@ -25,7 +25,7 @@ use parity_scale_codec::{Decode, Encode};
use polkadot_primitives::v1::{
Hash, CommittedCandidateReceipt, CandidateReceipt, CompactStatement,
EncodeAs, Signed, SigningContext, ValidatorIndex, ValidatorId,
UpwardMessage, Balance, ValidationCode, GlobalValidationData, LocalValidationData,
UpwardMessage, Balance, ValidationCode, PersistedValidationData, ValidationData,
HeadData, PoV, CollatorPair, Id as ParaId,
};
use polkadot_statement_table::{
@@ -118,10 +118,8 @@ pub struct FromTableMisbehavior {
pub struct ValidationOutputs {
/// The head-data produced by validation.
pub head_data: HeadData,
/// The global validation schedule.
pub global_validation_data: GlobalValidationData,
/// The local validation data.
pub local_validation_data: LocalValidationData,
/// The persisted validation data.
pub validation_data: PersistedValidationData,
/// Upward messages to the relay chain.
pub upward_messages: Vec<UpwardMessage>,
/// Fees paid to the validators of the relay-chain.
@@ -153,6 +151,8 @@ pub enum InvalidCandidate {
NewCodeTooLarge(u64),
/// Head-data is over the limit.
HeadDataTooLarge(u64),
/// Code upgrade triggered but not allowed.
CodeUpgradeNotAllowed,
}
/// Result of the validation of the candidate.
@@ -285,7 +285,7 @@ pub struct CollationGenerationConfig {
/// Collator's authentication key, so it can sign things.
pub key: CollatorPair,
/// Collation function.
pub collator: Box<dyn Fn(&GlobalValidationData, &LocalValidationData) -> Box<dyn Future<Output = Collation> + Unpin + Send> + Send + Sync>,
pub collator: Box<dyn Fn(&ValidationData) -> Box<dyn Future<Output = Collation> + Unpin + Send> + Send + Sync>,
/// The parachain that this collator collates for
pub para_id: ParaId,
}
+6 -6
View File
@@ -39,8 +39,8 @@ use keystore::KeyStorePtr;
use parity_scale_codec::Encode;
use pin_project::{pin_project, pinned_drop};
use polkadot_primitives::v1::{
CandidateEvent, CommittedCandidateReceipt, CoreState, EncodeAs, GlobalValidationData,
GroupRotationInfo, Hash, Id as ParaId, LocalValidationData, OccupiedCoreAssumption,
CandidateEvent, CommittedCandidateReceipt, CoreState, EncodeAs, PersistedValidationData,
GroupRotationInfo, Hash, Id as ParaId, ValidationData, OccupiedCoreAssumption,
SessionIndex, Signed, SigningContext, ValidationCode, ValidatorId, ValidatorIndex,
ValidatorPair,
};
@@ -184,8 +184,8 @@ specialize_requests! {
fn request_validators() -> Vec<ValidatorId>; Validators;
fn request_validator_groups() -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo); ValidatorGroups;
fn request_availability_cores() -> Vec<CoreState>; AvailabilityCores;
fn request_global_validation_data() -> GlobalValidationData; GlobalValidationData;
fn request_local_validation_data(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<LocalValidationData>; LocalValidationData;
fn request_full_validation_data(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationData>; FullValidationData;
fn request_persisted_validation_data(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<PersistedValidationData>; PersistedValidationData;
fn request_session_index_for_child() -> SessionIndex; SessionIndexForChild;
fn request_validation_code(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationCode>; ValidationCode;
fn request_candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt>; CandidatePendingAvailability;
@@ -267,8 +267,8 @@ specialize_requests_ctx! {
fn request_validators_ctx() -> Vec<ValidatorId>; Validators;
fn request_validator_groups_ctx() -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo); ValidatorGroups;
fn request_availability_cores_ctx() -> Vec<CoreState>; AvailabilityCores;
fn request_global_validation_data_ctx() -> GlobalValidationData; GlobalValidationData;
fn request_local_validation_data_ctx(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<LocalValidationData>; LocalValidationData;
fn request_full_validation_data_ctx(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationData>; FullValidationData;
fn request_persisted_validation_data_ctx(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<PersistedValidationData>; PersistedValidationData;
fn request_session_index_for_child_ctx() -> SessionIndex; SessionIndexForChild;
fn request_validation_code_ctx(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationCode>; ValidationCode;
fn request_candidate_pending_availability_ctx(para_id: ParaId) -> Option<CommittedCandidateReceipt>; CandidatePendingAvailability;
+21 -13
View File
@@ -33,9 +33,9 @@ use polkadot_node_primitives::{
use polkadot_primitives::v1::{
AvailableData, BackedCandidate, BlockNumber, CandidateDescriptor, CandidateEvent,
CandidateReceipt, CollatorId, CommittedCandidateReceipt,
CoreState, ErasureChunk, GlobalValidationData, GroupRotationInfo,
Hash, Id as ParaId, LocalValidationData, OccupiedCoreAssumption, OmittedValidationData, PoV,
SessionIndex, SignedAvailabilityBitfield, ValidationCode, ValidatorId, ValidatorIndex,
CoreState, ErasureChunk, GroupRotationInfo, Hash, Id as ParaId,
OccupiedCoreAssumption, PersistedValidationData, PoV, SessionIndex, SignedAvailabilityBitfield,
TransientValidationData, ValidationCode, ValidatorId, ValidationData, ValidatorIndex,
ValidatorSignature,
};
use std::sync::Arc;
@@ -107,7 +107,7 @@ pub struct ValidationFailed(pub String);
pub enum CandidateValidationMessage {
/// Validate a candidate with provided parameters using relay-chain state.
///
/// This will implicitly attempt to gather the `OmittedValidationData` and `ValidationCode`
/// This will implicitly attempt to gather the `PersistedValidationData` and `ValidationCode`
/// from the runtime API of the chain, based on the `relay_parent`
/// of the `CandidateDescriptor`.
///
@@ -120,10 +120,12 @@ pub enum CandidateValidationMessage {
),
/// Validate a candidate with provided, exhaustive parameters for validation.
///
/// Explicitly provide the `OmittedValidationData` and `ValidationCode` so this can do full
/// validation without needing to access the state of the relay-chain.
/// Explicitly provide the `PersistedValidationData` and `ValidationCode` so this can do full
/// validation without needing to access the state of the relay-chain. Optionally provide the
/// `TransientValidationData` for further checks on the outputs.
ValidateFromExhaustive(
OmittedValidationData,
PersistedValidationData,
Option<TransientValidationData>,
ValidationCode,
CandidateDescriptor,
Arc<PoV>,
@@ -136,7 +138,7 @@ impl CandidateValidationMessage {
pub fn relay_parent(&self) -> Option<Hash> {
match self {
Self::ValidateFromChainState(_, _, _) => None,
Self::ValidateFromExhaustive(_, _, _, _, _) => None,
Self::ValidateFromExhaustive(_, _, _, _, _, _) => None,
}
}
}
@@ -357,15 +359,21 @@ pub enum RuntimeApiRequest {
ValidatorGroups(RuntimeApiSender<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo)>),
/// Get information on all availability cores.
AvailabilityCores(RuntimeApiSender<Vec<CoreState>>),
/// Get the global validation data.
GlobalValidationData(RuntimeApiSender<GlobalValidationData>),
/// Get the local validation data for a particular para, taking the given
/// Get the persisted validation data for a particular para, taking the given
/// `OccupiedCoreAssumption`, which will inform on how the validation data should be computed
/// if the para currently occupies a core.
LocalValidationData(
PersistedValidationData(
ParaId,
OccupiedCoreAssumption,
RuntimeApiSender<Option<LocalValidationData>>,
RuntimeApiSender<Option<PersistedValidationData>>,
),
/// Get the full validation data for a particular para, taking the given
/// `OccupiedCoreAssumption`, which will inform on how the validation data should be computed
/// if the para currently occupies a core.
FullValidationData(
ParaId,
OccupiedCoreAssumption,
RuntimeApiSender<Option<ValidationData>>,
),
/// Get the session index that a child of the block will have.
SessionIndexForChild(RuntimeApiSender<SessionIndex>),
+9 -13
View File
@@ -28,6 +28,8 @@ use serde::{Serialize, Deserialize};
#[cfg(feature = "std")]
use sp_core::bytes;
use polkadot_core_primitives::Hash;
/// Block number type used by the relay chain.
pub use polkadot_core_primitives::BlockNumber as RelayChainBlockNumber;
@@ -228,22 +230,16 @@ pub struct UpwardMessage {
#[derive(PartialEq, Eq, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Encode))]
pub struct ValidationParams {
/// The collation body.
pub block_data: BlockData,
/// Previous head-data.
pub parent_head: HeadData,
/// The maximum code size permitted, in bytes.
pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes.
pub max_head_data_size: u32,
/// The collation body.
pub block_data: BlockData,
/// The current relay-chain block number.
pub relay_chain_height: polkadot_core_primitives::BlockNumber,
/// Whether a code upgrade is allowed or not, and at which height the upgrade
/// would be applied after, if so. The parachain logic should apply any upgrade
/// issued in this block after the first block
/// with `relay_chain_height` at least this value, if `Some`. if `None`, issue
/// no upgrade.
pub code_upgrade_allowed: Option<polkadot_core_primitives::BlockNumber>,
pub relay_chain_height: RelayChainBlockNumber,
/// The list of MQC heads for the inbound HRMP channels paired with the sender para ids. This
/// vector is sorted ascending by the para id and doesn't contain multiple entries with the same
/// sender.
pub hrmp_mqc_heads: Vec<(Id, Hash)>,
}
/// The result of parachain validation.
@@ -12,7 +12,6 @@ codec = { package = "parity-scale-codec", version = "1.3.4", default-features =
parachain = { package = "polkadot-parachain", path = ".." }
adder = { package = "test-parachain-adder", path = "adder" }
halt = { package = "test-parachain-halt", path = "halt" }
code-upgrader = { package = "test-parachain-code-upgrader", path = "code-upgrader" }
[dev-dependencies]
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -22,5 +21,4 @@ default = [ "std" ]
std = [
"adder/std",
"halt/std",
"code-upgrader/std",
]
@@ -1,27 +0,0 @@
[package]
name = "test-parachain-code-upgrader"
version = "0.7.22"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Test parachain which can upgrade code"
edition = "2018"
build = "build.rs"
[dependencies]
parachain = { package = "polkadot-parachain", path = "../../", default-features = false, features = [ "wasm-api" ] }
codec = { package = "parity-scale-codec", version = "1.1.0", default-features = false, features = ["derive"] }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
tiny-keccak = "1.5.0"
dlmalloc = { version = "0.1.3", features = [ "global" ] }
# We need to make sure the global allocator is disabled until we have support of full substrate externalities
runtime-io = { package = "sp-io", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, features = [ "disable_allocator" ] }
[build-dependencies]
wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.6" }
[features]
default = [ "std" ]
std = [
"parachain/std",
"sp-std/std",
]
@@ -1,25 +0,0 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use wasm_builder_runner::WasmBuilder;
fn main() {
WasmBuilder::new()
.with_current_project()
.with_wasm_builder_from_crates("2.0.0")
.export_heap_base()
.build()
}
@@ -1,163 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Test parachain WASM which implements code ugprades.
#![no_std]
#![cfg_attr(not(feature = "std"), feature(core_intrinsics, lang_items, core_panic_info, alloc_error_handler))]
use codec::{Encode, Decode};
use parachain::primitives::{RelayChainBlockNumber, ValidationCode};
#[cfg(not(feature = "std"))]
mod wasm_validation;
#[cfg(not(feature = "std"))]
#[global_allocator]
static ALLOC: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc;
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "std")]
/// Wasm binary unwrapped. If built with `BUILD_DUMMY_WASM_BINARY`, the function panics.
pub fn wasm_binary_unwrap() -> &'static [u8] {
WASM_BINARY.expect("Development wasm binary is not available. Testing is only \
supported with the flag disabled.")
}
#[derive(Encode, Decode, Clone, Default)]
pub struct State {
/// The current code that is "active" in this chain.
pub code: ValidationCode,
/// Code upgrade that is pending.
pub pending_code: Option<(ValidationCode, RelayChainBlockNumber)>,
}
/// Head data for this parachain.
#[derive(Default, Clone, Hash, Eq, PartialEq, Encode, Decode)]
pub struct HeadData {
/// Block number
pub number: u64,
/// parent block keccak256
pub parent_hash: [u8; 32],
/// hash of post-execution state.
pub post_state: [u8; 32],
}
impl HeadData {
pub fn hash(&self) -> [u8; 32] {
tiny_keccak::keccak256(&self.encode())
}
}
/// Block data for this parachain.
#[derive(Default, Clone, Encode, Decode)]
pub struct BlockData {
/// State to begin from.
pub state: State,
/// Code to upgrade to.
pub new_validation_code: Option<ValidationCode>,
}
pub fn hash_state(state: &State) -> [u8; 32] {
tiny_keccak::keccak256(state.encode().as_slice())
}
#[derive(Debug)]
pub enum Error {
/// Start state mismatched with parent header's state hash.
StateMismatch,
/// New validation code too large.
NewCodeTooLarge,
/// Code upgrades not allowed at this time.
CodeUpgradeDisallowed,
}
pub struct ValidationResult {
/// The new head data.
pub head_data: HeadData,
/// The new validation code.
pub new_validation_code: Option<ValidationCode>,
}
pub struct RelayChainParams {
/// Whether a code upgrade is allowed and at what relay-chain block number
/// to process it after.
pub code_upgrade_allowed: Option<RelayChainBlockNumber>,
/// The maximum code size allowed for an upgrade.
pub max_code_size: u32,
/// The relay-chain block number.
pub relay_chain_block_number: RelayChainBlockNumber,
}
/// Execute a block body on top of given parent head, producing new parent head
/// if valid.
pub fn execute(
parent_hash: [u8; 32],
parent_head: HeadData,
block_data: BlockData,
relay_params: &RelayChainParams,
) -> Result<ValidationResult, Error> {
debug_assert_eq!(parent_hash, parent_head.hash());
if hash_state(&block_data.state) != parent_head.post_state {
return Err(Error::StateMismatch);
}
let mut new_state = block_data.state;
if let Some((pending_code, after)) = new_state.pending_code.take() {
if after <= relay_params.relay_chain_block_number {
// code applied.
new_state.code = pending_code;
} else {
// reinstate.
new_state.pending_code = Some((pending_code, after));
}
}
let new_validation_code = if let Some(ref new_validation_code) = block_data.new_validation_code {
if new_validation_code.0.len() as u32 > relay_params.max_code_size {
return Err(Error::NewCodeTooLarge);
}
// replace the code if allowed and we don't have an upgrade pending.
match (new_state.pending_code.is_some(), relay_params.code_upgrade_allowed) {
(_, None) => return Err(Error::CodeUpgradeDisallowed),
(false, Some(after)) => {
new_state.pending_code = Some((new_validation_code.clone(), after));
Some(new_validation_code.clone())
}
(true, Some(_)) => None,
}
} else {
None
};
let head_data = HeadData {
number: parent_head.number + 1,
parent_hash,
post_state: hash_state(&new_state),
};
Ok(ValidationResult {
head_data,
new_validation_code: new_validation_code,
})
}
@@ -1,57 +0,0 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! WASM validation for adder parachain.
use crate::{HeadData, BlockData, RelayChainParams};
use core::{intrinsics, panic};
use parachain::primitives::{ValidationResult, HeadData as GenericHeadData};
use codec::{Encode, Decode};
#[no_mangle]
pub extern fn validate_block(params: *const u8, len: usize) -> u64 {
let params = unsafe { parachain::load_params(params, len) };
let parent_head = HeadData::decode(&mut &params.parent_head.0[..])
.expect("invalid parent head format.");
let block_data = BlockData::decode(&mut &params.block_data.0[..])
.expect("invalid block data format.");
let parent_hash = tiny_keccak::keccak256(&params.parent_head.0[..]);
let res = crate::execute(
parent_hash,
parent_head,
block_data,
&RelayChainParams {
code_upgrade_allowed: params.code_upgrade_allowed,
max_code_size: params.max_code_size,
relay_chain_block_number: params.relay_chain_height,
},
);
match res {
Ok(output) => parachain::write_result(
&ValidationResult {
head_data: GenericHeadData(output.head_data.encode()),
new_validation_code: output.new_validation_code,
upward_messages: sp_std::vec::Vec::new(),
processed_downward_messages: 0,
}
),
Err(_) => panic!("execution failure"),
}
}
@@ -72,10 +72,8 @@ pub fn execute_good_on_parent() {
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
hrmp_mqc_heads: Vec::new(),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
@@ -112,10 +110,8 @@ fn execute_good_chain_on_parent() {
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: number as RelayChainBlockNumber + 1,
code_upgrade_allowed: None,
hrmp_mqc_heads: Vec::new(),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
@@ -153,10 +149,8 @@ fn execute_bad_on_parent() {
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
hrmp_mqc_heads: Vec::new(),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
@@ -1,217 +0,0 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Basic parachain that adds a number as part of its state.
use parachain::primitives::{
BlockData as GenericBlockData,
HeadData as GenericHeadData,
ValidationParams, ValidationCode,
};
use codec::{Decode, Encode};
use code_upgrader::{hash_state, HeadData, BlockData, State};
#[test]
pub fn execute_good_no_upgrade() {
let pool = parachain::wasm_executor::ValidationPool::new();
let parent_head = HeadData {
number: 0,
parent_hash: [0; 32],
post_state: hash_state(&State::default()),
};
let block_data = BlockData {
state: State::default(),
new_validation_code: None,
};
let ret = parachain::wasm_executor::validate_candidate(
code_upgrader::wasm_binary_unwrap(),
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
).unwrap();
let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap();
assert!(ret.new_validation_code.is_none());
assert_eq!(new_head.number, 1);
assert_eq!(new_head.parent_hash, parent_head.hash());
assert_eq!(new_head.post_state, hash_state(&State::default()));
}
#[test]
pub fn execute_good_with_upgrade() {
let pool = parachain::wasm_executor::ValidationPool::new();
let parent_head = HeadData {
number: 0,
parent_hash: [0; 32],
post_state: hash_state(&State::default()),
};
let block_data = BlockData {
state: State::default(),
new_validation_code: Some(ValidationCode(vec![1, 2, 3])),
};
let ret = parachain::wasm_executor::validate_candidate(
code_upgrader::wasm_binary_unwrap(),
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: Some(20),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
).unwrap();
let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap();
assert_eq!(ret.new_validation_code.unwrap(), ValidationCode(vec![1, 2, 3]));
assert_eq!(new_head.number, 1);
assert_eq!(new_head.parent_hash, parent_head.hash());
assert_eq!(
new_head.post_state,
hash_state(&State {
code: ValidationCode::default(),
pending_code: Some((ValidationCode(vec![1, 2, 3]), 20)),
}),
);
}
#[test]
#[should_panic]
pub fn code_upgrade_not_allowed() {
let pool = parachain::wasm_executor::ValidationPool::new();
let parent_head = HeadData {
number: 0,
parent_hash: [0; 32],
post_state: hash_state(&State::default()),
};
let block_data = BlockData {
state: State::default(),
new_validation_code: Some(ValidationCode(vec![1, 2, 3])),
};
parachain::wasm_executor::validate_candidate(
code_upgrader::wasm_binary_unwrap(),
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
).unwrap();
}
#[test]
pub fn applies_code_upgrade_after_delay() {
let pool = parachain::wasm_executor::ValidationPool::new();
let (new_head, state) = {
let parent_head = HeadData {
number: 0,
parent_hash: [0; 32],
post_state: hash_state(&State::default()),
};
let block_data = BlockData {
state: State::default(),
new_validation_code: Some(ValidationCode(vec![1, 2, 3])),
};
let ret = parachain::wasm_executor::validate_candidate(
code_upgrader::wasm_binary_unwrap(),
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: Some(2),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
).unwrap();
let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap();
let parent_hash = parent_head.hash();
let state = State {
code: ValidationCode::default(),
pending_code: Some((ValidationCode(vec![1, 2, 3]), 2)),
};
assert_eq!(ret.new_validation_code.unwrap(), ValidationCode(vec![1, 2, 3]));
assert_eq!(new_head.number, 1);
assert_eq!(new_head.parent_hash, parent_hash);
assert_eq!(new_head.post_state, hash_state(&state));
(new_head, state)
};
{
let parent_head = new_head;
let block_data = BlockData {
state,
new_validation_code: None,
};
let ret = parachain::wasm_executor::validate_candidate(
code_upgrader::wasm_binary_unwrap(),
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 2,
code_upgrade_allowed: None,
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
).unwrap();
let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap();
assert!(ret.new_validation_code.is_none());
assert_eq!(new_head.number, 2);
assert_eq!(new_head.parent_hash, parent_head.hash());
assert_eq!(
new_head.post_state,
hash_state(&State {
code: ValidationCode(vec![1, 2, 3]),
pending_code: None,
}),
);
}
}
@@ -15,7 +15,6 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
mod adder;
mod code_upgrader;
mod wasm_executor;
use parachain::wasm_executor::run_worker;
@@ -31,10 +31,8 @@ fn terminates_on_timeout() {
ValidationParams {
block_data: BlockData(Vec::new()),
parent_head: Default::default(),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
hrmp_mqc_heads: Vec::new(),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
@@ -61,10 +59,8 @@ fn parallel_execution() {
ValidationParams {
block_data: BlockData(Vec::new()),
parent_head: Default::default(),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
hrmp_mqc_heads: Vec::new(),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool2),
sp_core::testing::TaskExecutor::new(),
@@ -74,10 +70,8 @@ fn parallel_execution() {
ValidationParams {
block_data: BlockData(Vec::new()),
parent_head: Default::default(),
max_code_size: 1024,
max_head_data_size: 1024,
relay_chain_height: 1,
code_upgrade_allowed: None,
hrmp_mqc_heads: Vec::new(),
},
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
sp_core::testing::TaskExecutor::new(),
+90 -59
View File
@@ -60,7 +60,7 @@ 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,
persisted_validation_data_hash: &Hash,
pov_hash: &Hash,
) -> [u8; 100] {
// 32-byte hash length is protected in a test below.
@@ -68,7 +68,7 @@ pub fn collator_signature_payload<H: AsRef<[u8]>>(
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(validation_data_hash.as_ref());
payload[36..68].copy_from_slice(persisted_validation_data_hash.as_ref());
payload[68..100].copy_from_slice(pov_hash.as_ref());
payload
@@ -77,7 +77,7 @@ 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,
persisted_validation_data_hash: &Hash,
pov_hash: &Hash,
collator: &CollatorId,
signature: &CollatorSignature,
@@ -85,7 +85,7 @@ fn check_collator_signature<H: AsRef<[u8]>>(
let payload = collator_signature_payload(
relay_parent,
para_id,
validation_data_hash,
persisted_validation_data_hash,
pov_hash,
);
@@ -96,14 +96,6 @@ 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, Hash))]
@@ -114,16 +106,15 @@ pub struct CandidateDescriptor<H = Hash> {
pub relay_parent: H,
/// The collator's sr25519 public key.
pub collator: CollatorId,
/// The blake2-256 hash of the validation data. This is extra data derived from
/// The blake2-256 hash of the persisted 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,
pub persisted_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> {
@@ -132,7 +123,7 @@ impl<H: AsRef<[u8]>> CandidateDescriptor<H> {
check_collator_signature(
&self.relay_parent,
&self.para_id,
&self.validation_data_hash,
&self.persisted_validation_data_hash,
&self.pov_hash,
&self.collator,
&self.signature,
@@ -168,10 +159,11 @@ impl<H> CandidateReceipt<H> {
pub struct FullCandidateReceipt<H = Hash, N = BlockNumber> {
/// The inner candidate receipt.
pub inner: CandidateReceipt<H>,
/// The global validation schedule.
pub global_validation: GlobalValidationData<N>,
/// The local validation data.
pub local_validation: LocalValidationData<N>,
/// The validation data derived from the relay-chain state at that
/// point. The hash of the persisted validation data should
/// match the `persisted_validation_data_hash` in the descriptor
/// of the receipt.
pub validation_data: ValidationData<N>,
}
/// A candidate-receipt with commitments directly included.
@@ -224,17 +216,78 @@ impl Ord for CommittedCandidateReceipt {
}
}
/// Extra data that is needed along with the other fields in a `CandidateReceipt`
/// to fully validate the candidate. These fields are parachain-specific.
/// The validation data provide information about how to validate both the inputs and
/// outputs of a candidate.
///
/// There are two types of validation data: persisted and transient.
/// Their respective sections of the guide elaborate on their functionality in more detail.
///
/// This information is derived from the chain state and will vary from para to para,
/// although some of the fields may be the same for every para.
///
/// Persisted validation data are generally derived from some relay-chain state to form inputs
/// to the validation function, and as such need to be persisted by the availability system to
/// avoid dependence on availability of the relay-chain state. The backing phase of the
/// inclusion pipeline ensures that everything that is included in a valid fork of the
/// relay-chain already adheres to the transient constraints.
///
/// The validation data also serve the purpose of giving collators a means of ensuring that
/// their produced candidate and the commitments submitted to the relay-chain alongside it
/// will pass the checks done by the relay-chain when backing, and give validators
/// the same understanding when determining whether to second or attest to a candidate.
///
/// Since the commitments of the validation function are checked by the
/// relay-chain, secondary checkers can rely on the invariant that the relay-chain
/// only includes para-blocks for which these checks have already been done. As such,
/// there is no need for the validation data used to inform validators and collators about
/// the checks the relay-chain will perform to be persisted by the availability system.
/// Nevertheless, we expose it so the backing validators can validate the outputs of a
/// candidate before voting to submit it to the relay-chain and so collators can
/// collate candidates that satisfy the criteria implied these transient validation data.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct LocalValidationData<N = BlockNumber> {
pub struct ValidationData<N = BlockNumber> {
/// The persisted validation data.
pub persisted: PersistedValidationData<N>,
/// The transient validation data.
pub transient: TransientValidationData<N>,
}
/// Validation data that needs to be persisted for secondary checkers.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct PersistedValidationData<N = BlockNumber> {
/// The parent head-data.
pub parent_head: HeadData,
/// The relay-chain block number this is in the context of.
pub block_number: N,
/// The list of MQC heads for the inbound channels paired with the sender para ids. This
/// vector is sorted ascending by the para id and doesn't contain multiple entries with the same
/// sender.
pub hrmp_mqc_heads: Vec<(Id, Hash)>,
}
impl<N: Encode> PersistedValidationData<N> {
/// Compute the blake2-256 hash of the persisted validation data.
pub fn hash(&self) -> Hash {
BlakeTwo256::hash_of(self)
}
}
/// Validation data for checking outputs of the validation-function.
/// As such, they also inform the collator about how to construct the candidate.
///
/// These are transient because they are not necessary beyond the point where the
/// candidate is backed.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct TransientValidationData<N = BlockNumber> {
/// The maximum code size permitted, in bytes.
pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes.
pub max_head_data_size: u32,
/// The balance of the parachain at the moment of validation.
pub balance: Balance,
/// The blake2-256 hash of the validation code used to execute the candidate.
pub validation_code_hash: Hash,
/// Whether the parachain is allowed to upgrade its validation code.
///
/// This is `Some` if so, and contains the number of the minimum relay-chain
@@ -249,21 +302,6 @@ pub struct LocalValidationData<N = BlockNumber> {
pub code_upgrade_allowed: Option<N>,
}
/// Extra data that is needed along with the other fields in a `CandidateReceipt`
/// to fully validate the candidate.
///
/// 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 GlobalValidationData<N = BlockNumber> {
/// The maximum code size permitted, in bytes.
pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes.
pub max_head_data_size: u32,
/// The relay-chain block number this is in the context of.
pub block_number: N,
}
/// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default, Hash))]
@@ -434,25 +472,14 @@ pub enum CoreOccupied {
Parachain,
}
/// Validation data omitted from most candidate descriptor structs, as it can be derived from the
/// relay-parent.
#[derive(Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug, Default))]
pub struct OmittedValidationData {
/// The global validation schedule.
pub global_validation: GlobalValidationData,
/// The local validation data.
pub local_validation: LocalValidationData,
}
/// This is the data we keep available for each candidate included in the relay chain.
#[derive(Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub struct AvailableData {
/// The Proof-of-Validation of the candidate.
pub pov: PoV,
/// The omitted validation data.
pub omitted_validation: OmittedValidationData,
/// The persisted validation data needed for secondary checks.
pub validation_data: PersistedValidationData,
}
/// A helper data-type for tracking validator-group rotations.
@@ -622,17 +649,21 @@ sp_api::decl_runtime_apis! {
/// cores can have paras assigned to them.
fn availability_cores() -> Vec<CoreState<N>>;
/// 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_data() -> GlobalValidationData<N>;
/// Yields the full validation data for the given ParaId along with an assumption that
/// should be used if the para currently occupieds a core.
///
/// Returns `None` if either the para is not registered or the assumption is `Freed`
/// and the para already occupies a core.
fn full_validation_data(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<ValidationData<N>>;
/// Yields the LocalValidationData for the given ParaId along with an assumption that
/// Yields the persisted validation data for the given ParaId along with an assumption that
/// should be used if the para currently occupies a core.
///
/// Returns `None` if either the para is not registered or the assumption is `Freed`
/// and the para already occupies a core.
fn local_validation_data(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<LocalValidationData<N>>;
fn persisted_validation_data(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<PersistedValidationData<N>>;
/// Returns the session index expected at a child of the block.
///
@@ -24,7 +24,7 @@ Upon receiving a validation request, the first thing the candidate validation su
### Determining Parameters
For a [`CandidateValidationMessage`][CVM]`::ValidateFromExhaustive`, these parameters are exhaustively provided. The [`OmittedValidationData`](../../types/availability.md#omittedvalidationdata) can be deconstructed into the validation data.
For a [`CandidateValidationMessage`][CVM]`::ValidateFromExhaustive`, these parameters are exhaustively provided. The [`TransientValidationData`](../../types/candidate.md#transientvalidationdata) is optional, and is used to perform further checks on the outputs of validation.
For a [`CandidateValidationMessage`][CVM]`::ValidateFromChainState`, some more work needs to be done. Due to the uncertainty of Availability Cores (implemented in the [`Scheduler`](../../runtime/scheduler.md) module of the runtime), a candidate at a particular relay-parent and for a particular para may have two different valid validation-data to be executed under depending on what is assumed to happen if the para is occupying a core at the onset of the new block. This is encoded as an `OccupiedCoreAssumption` in the runtime API.
@@ -40,7 +40,7 @@ Once we have all parameters, we can spin up a background task to perform the val
* The collator signature is valid
* The PoV provided matches the `pov_hash` field of the descriptor
After that, we can invoke the validation function. Lastly, we do some final checks on the output:
After that, we can invoke the validation function. Lastly, if available, we do some final checks on the output using the `TransientValidationData`:
* The produced head-data is no larger than the maximum allowed.
* The produced code upgrade, if any, is no larger than the maximum allowed, and a code upgrade was allowed to be signaled.
* The amount and size of produced upward messages is not too large.
@@ -77,7 +77,7 @@ struct CandidateDescriptor {
/// The blake2-256 hash of the persisted validation data. These are extra parameters
/// derived from relay-chain state that influence the validity of the block which
/// must also be kept available for secondary checkers.
validation_data_hash: Hash,
persisted_validation_data_hash: Hash,
/// The blake2-256 hash of the pov-block.
pov_hash: Hash,
/// Signature on blake2-256 of components of this receipt:
@@ -119,23 +119,13 @@ Validation data that needs to be persisted for secondary checkers. See the secti
struct PersistedValidationData {
/// The parent head-data.
parent_head: HeadData,
/// Whether the parachain is allowed to upgrade its validation code.
///
/// This is `Some` if so, and contains the number of the minimum relay-chain
/// height at which the upgrade will be applied, if an upgrade is signaled
/// now.
///
/// A parachain should enact its side of the upgrade at the end of the first
/// parablock executing in the context of a relay-chain block with at least this
/// height. This may be equal to the current perceived relay-chain block height, in
/// which case the code upgrade should be applied at the end of the signaling
/// block.
///
/// This informs a relay-chain backing check and the parachain logic.
code_upgrade_allowed: Option<BlockNumber>,
/// The relay-chain block number this is in the context of. This informs the collator.
block_number: BlockNumber,
/// The relay-chain
/// The list of MQC heads for the inbound channels paired with the sender para ids. This
/// vector is sorted ascending by the para id and doesn't contain multiple entries with the same
/// sender.
hrmp_mqc_heads: Vec<(ParaId, Hash)>,
}
```
@@ -155,10 +145,20 @@ struct TransientValidationData {
max_head_data_size: u32,
/// The balance of the parachain at the moment of validation.
balance: Balance,
/// The list of MQC heads for the inbound channels paired with the sender para ids. This
/// vector is sorted ascending by the para id and doesn't contain multiple entries with the same
/// sender. This informs the collator.
hrmp_mqc_heads: Vec<(ParaId, Hash)>,
/// Whether the parachain is allowed to upgrade its validation code.
///
/// This is `Some` if so, and contains the number of the minimum relay-chain
/// height at which the upgrade will be applied, if an upgrade is signaled
/// now.
///
/// A parachain should enact its side of the upgrade at the end of the first
/// parablock executing in the context of a relay-chain block with at least this
/// height. This may be equal to the current perceived relay-chain block height, in
/// which case the code upgrade should be applied at the end of the signaling
/// block.
///
/// This informs a relay-chain backing check and the parachain logic.
code_upgrade_allowed: Option<BlockNumber>,
}
```
@@ -219,8 +219,8 @@ This struct encapsulates the outputs of candidate validation.
struct ValidationOutputs {
/// The head-data produced by validation.
head_data: HeadData,
/// The validation data, persisted and transient.
validation_data: ValidationData,
/// The validation data, persisted.
validation_data: PersistedValidationData,
/// Messages directed to other paras routed via the relay chain.
horizontal_messages: Vec<OutboundHrmpMessage>,
/// Upwards messages to the relay chain.
@@ -393,11 +393,13 @@ enum CandidateValidationMessage {
/// the para is not free at the relay-parent, an error is returned.
ValidateFromChainState(CandidateDescriptor, PoV, ResponseChannel<Result<ValidationResult>>),
/// Validate a candidate with provided parameters. Explicitly provide the `OmittedValidationData`
/// Validate a candidate with provided parameters. Explicitly provide the `PersistedValidationData`
/// and `ValidationCode` so this can do full validation without needing to access the state of
/// the relay-chain.
/// the relay-chain. Optionally provide the `TransientValidationData` which will lead to checks
/// on the output.
ValidateFromExhaustive(
OmittedValidationData,
PersistedValidationData,
Option<TransientValidationData>,
ValidationCode,
CandidateDescriptor,
PoV,
@@ -19,13 +19,12 @@
//! Configuration can change only at session boundaries and is buffered until then.
use sp_std::prelude::*;
use primitives::v1::{ValidatorId, GlobalValidationData};
use primitives::v1::ValidatorId;
use frame_support::{
decl_storage, decl_module, decl_error,
dispatch::DispatchResult,
weights::{DispatchClass, Weight},
};
use sp_runtime::traits::One;
use codec::{Encode, Decode};
use frame_system::ensure_root;
@@ -220,16 +219,6 @@ 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: <frame_system::Module<T>>::block_number() - One::one(),
}
}
}
#[cfg(test)]
+26 -32
View File
@@ -22,7 +22,6 @@
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, CommittedCandidateReceipt,
@@ -435,9 +434,8 @@ 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) {
let persisted_validation_data =
match crate::util::make_persisted_validation_data::<T>(para_id) {
Some(l) => l,
None => {
// We don't want to error out here because it will
@@ -445,16 +443,12 @@ impl<T: Trait> Module<T> {
// doing anything.
return Ok(Vec::new());
}
}
);
};
let expected = validation_data_hash(
&global_validation_data,
&local_validation_data,
);
let expected = persisted_validation_data.hash();
ensure!(
expected == candidate.descriptor().validation_data_hash,
expected == candidate.descriptor().persisted_validation_data_hash,
Error::<T>::ValidationDataHashMismatch,
);
}
@@ -722,7 +716,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.persisted_validation_data_hash,
&candidate.descriptor.pov_hash,
);
@@ -851,7 +845,7 @@ mod tests {
head_data: HeadData,
pov_hash: Hash,
relay_parent: Hash,
validation_data_hash: Hash,
persisted_validation_data_hash: Hash,
new_validation_code: Option<ValidationCode>,
}
@@ -862,7 +856,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,
persisted_validation_data_hash: self.persisted_validation_data_hash,
..Default::default()
},
commitments: CandidateCommitments {
@@ -875,9 +869,9 @@ 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))
let persisted_validation_data
= crate::util::make_persisted_validation_data::<Test>(para_id)?;
Some(persisted_validation_data.hash())
}
#[test]
@@ -1306,7 +1300,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
collator_sign_candidate(
@@ -1338,14 +1332,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(),
persisted_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(),
persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(),
..Default::default()
}.build();
@@ -1392,7 +1386,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
collator_sign_candidate(
@@ -1427,7 +1421,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
collator_sign_candidate(
@@ -1459,7 +1453,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(),
persisted_validation_data_hash: make_vdata_hash(thread_a).unwrap(),
..Default::default()
}.build();
@@ -1497,7 +1491,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(),
persisted_validation_data_hash: make_vdata_hash(thread_a).unwrap(),
..Default::default()
}.build();
@@ -1534,7 +1528,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
@@ -1580,7 +1574,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
@@ -1619,7 +1613,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
@@ -1660,7 +1654,7 @@ mod tests {
para_id: chain_a,
relay_parent: System::parent_hash(),
pov_hash: Hash::from([1; 32]),
validation_data_hash: [42u8; 32].into(),
persisted_validation_data_hash: [42u8; 32].into(),
..Default::default()
}.build();
@@ -1750,7 +1744,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
..Default::default()
}.build();
collator_sign_candidate(
@@ -1762,7 +1756,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(),
persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(),
..Default::default()
}.build();
collator_sign_candidate(
@@ -1774,7 +1768,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(),
persisted_validation_data_hash: make_vdata_hash(thread_a).unwrap(),
..Default::default()
}.build();
collator_sign_candidate(
@@ -1906,7 +1900,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(),
persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(),
new_validation_code: Some(vec![1, 2, 3].into()),
..Default::default()
}.build();
+2
View File
@@ -38,6 +38,8 @@ pub mod validity;
pub mod runtime_api_impl;
mod util;
#[cfg(test)]
mod mock;
+2 -33
View File
@@ -26,10 +26,10 @@
use sp_std::prelude::*;
#[cfg(feature = "std")]
use sp_std::marker::PhantomData;
use sp_runtime::traits::{One, BlakeTwo256, Hash as HashT, Saturating};
use primitives::v1::{
Id as ParaId, ValidationCode, HeadData, LocalValidationData,
Id as ParaId, ValidationCode, HeadData,
};
use sp_runtime::traits::One;
use frame_support::{
decl_storage, decl_module, decl_error,
traits::Get,
@@ -550,37 +550,6 @@ 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 = <frame_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(&para_id)?,
balance: 0,
validation_code_hash: BlakeTwo256::hash_of(
&Self::current_code(&para_id)?
),
code_upgrade_allowed,
})
}
}
#[cfg(test)]
@@ -19,10 +19,10 @@
use sp_std::prelude::*;
use primitives::v1::{
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, GlobalValidationData,
Id as ParaId, OccupiedCoreAssumption, LocalValidationData, SessionIndex, ValidationCode,
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, ValidationData,
Id as ParaId, OccupiedCoreAssumption, SessionIndex, ValidationCode,
CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex,
GroupIndex, CandidateEvent,
GroupIndex, CandidateEvent, PersistedValidationData,
};
use sp_runtime::traits::Zero;
use frame_support::debug;
@@ -161,36 +161,61 @@ pub fn availability_cores<T: initializer::Trait>() -> Vec<CoreState<T::BlockNumb
core_states
}
/// Implementation for the `global_validation_data` function of the runtime API.
pub fn global_validation_data<T: initializer::Trait>()
-> GlobalValidationData<T::BlockNumber>
{
<configuration::Module<T>>::global_validation_data()
}
/// Implementation for the `local_validation_data` function of the runtime API.
pub fn local_validation_data<T: initializer::Trait>(
fn with_assumption<Trait, T, F>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<LocalValidationData<T::BlockNumber>> {
build: F,
) -> Option<T> where
Trait: inclusion::Trait,
F: FnOnce() -> Option<T>,
{
match assumption {
OccupiedCoreAssumption::Included => {
<inclusion::Module<T>>::force_enact(para_id);
<paras::Module<T>>::local_validation_data(para_id)
<inclusion::Module<Trait>>::force_enact(para_id);
build()
}
OccupiedCoreAssumption::TimedOut => {
<paras::Module<T>>::local_validation_data(para_id)
build()
}
OccupiedCoreAssumption::Free => {
if <inclusion::Module<T>>::pending_availability(para_id).is_some() {
if <inclusion::Module<Trait>>::pending_availability(para_id).is_some() {
None
} else {
<paras::Module<T>>::local_validation_data(para_id)
build()
}
}
}
}
/// Implementation for the `full_validation_data` function of the runtime API.
pub fn full_validation_data<T: initializer::Trait>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
)
-> Option<ValidationData<T::BlockNumber>>
{
with_assumption::<T, _, _>(
para_id,
assumption,
|| Some(ValidationData {
persisted: crate::util::make_persisted_validation_data::<T>(para_id)?,
transient: crate::util::make_transient_validation_data::<T>(para_id)?,
}),
)
}
/// Implementation for the `persisted_validation_data` function of the runtime API.
pub fn persisted_validation_data<T: initializer::Trait>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<PersistedValidationData<T::BlockNumber>> {
with_assumption::<T, _, _>(
para_id,
assumption,
|| crate::util::make_persisted_validation_data::<T>(para_id),
)
}
/// Implementation for the `session_index_for_child` function of the runtime API.
pub fn session_index_for_child<T: initializer::Trait>() -> SessionIndex {
// Just returns the session index from `inclusion`. Runtime APIs follow
@@ -208,26 +233,11 @@ pub fn validation_code<T: initializer::Trait>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<ValidationCode> {
let fetch = || {
<paras::Module<T>>::current_code(&para_id)
};
match assumption {
OccupiedCoreAssumption::Included => {
<inclusion::Module<T>>::force_enact(para_id);
fetch()
}
OccupiedCoreAssumption::TimedOut => {
fetch()
}
OccupiedCoreAssumption::Free => {
if <inclusion::Module<T>>::pending_availability(para_id).is_some() {
None
} else {
fetch()
}
}
}
with_assumption::<T, _, _>(
para_id,
assumption,
|| <paras::Module<T>>::current_code(&para_id),
)
}
/// Implementation for the `candidate_pending_availability` function of the runtime API.
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Utilities that don't belong to any particular module but may draw
//! on all modules.
use sp_runtime::traits::{One, Saturating};
use primitives::v1::{Id as ParaId, PersistedValidationData, TransientValidationData};
use sp_std::prelude::*;
use crate::{configuration, paras};
/// Make the persisted validation data for a particular parachain.
///
/// This ties together the storage of several modules.
pub fn make_persisted_validation_data<T: paras::Trait>(
para_id: ParaId,
) -> Option<PersistedValidationData<T::BlockNumber>> {
let relay_parent_number = <frame_system::Module<T>>::block_number() - One::one();
Some(PersistedValidationData {
parent_head: <paras::Module<T>>::para_head(&para_id)?,
block_number: relay_parent_number,
hrmp_mqc_heads: Vec::new(),
})
}
/// Make the transient validation data for a particular parachain.
///
/// This ties together the storage of several modules.
pub fn make_transient_validation_data<T: paras::Trait>(
para_id: ParaId,
) -> Option<TransientValidationData<T::BlockNumber>> {
let config = <configuration::Module<T>>::config();
let relay_parent_number = <frame_system::Module<T>>::block_number() - One::one();
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.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(TransientValidationData {
max_code_size: config.max_code_size,
max_head_data_size: config.max_head_data_size,
balance: 0,
code_upgrade_allowed,
})
}
+8 -7
View File
@@ -24,9 +24,9 @@ use sp_std::prelude::*;
use codec::Encode;
use primitives::v1::{
AccountId, AccountIndex, Balance, BlockNumber, Hash, Nonce, Signature, Moment,
GroupRotationInfo, CoreState, Id, GlobalValidationData, ValidationCode, CandidateEvent,
GroupRotationInfo, CoreState, Id, ValidationData, ValidationCode, CandidateEvent,
ValidatorId, ValidatorIndex, CommittedCandidateReceipt, OccupiedCoreAssumption,
LocalValidationData,
PersistedValidationData,
};
use runtime_common::{
SlowAdjustingFeeUpdate,
@@ -176,13 +176,14 @@ sp_api::impl_runtime_apis! {
runtime_api_impl::availability_cores::<Runtime>()
}
fn global_validation_data() -> GlobalValidationData<BlockNumber> {
runtime_api_impl::global_validation_data::<Runtime>()
fn full_validation_data(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<ValidationData<BlockNumber>> {
runtime_api_impl::full_validation_data::<Runtime>(para_id, assumption)
}
fn local_validation_data(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<LocalValidationData<BlockNumber>> {
runtime_api_impl::local_validation_data::<Runtime>(para_id, assumption)
fn persisted_validation_data(para_id: Id, assumption: OccupiedCoreAssumption)
-> Option<PersistedValidationData<BlockNumber>> {
runtime_api_impl::persisted_validation_data::<Runtime>(para_id, assumption)
}
fn session_index_for_child() -> SessionIndex {
+1 -3
View File
@@ -204,10 +204,8 @@ pub fn validate<'a>(
let params = ValidationParams {
parent_head: local_validation.parent_head.clone(),
block_data: pov_block.block_data.clone(),
max_code_size: global_validation.max_code_size,
max_head_data_size: global_validation.max_head_data_size,
relay_chain_height: global_validation.block_number,
code_upgrade_allowed: local_validation.code_upgrade_allowed,
hrmp_mqc_heads: Vec::new(),
};
// TODO: remove when ext does not do this.