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
+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;