mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-06 04:28:01 +00:00
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:
committed by
GitHub
parent
3395044402
commit
262574fc49
Generated
-14
@@ -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",
|
||||
]
|
||||
|
||||
@@ -70,7 +70,6 @@ members = [
|
||||
"parachain/test-parachains",
|
||||
"parachain/test-parachains/adder",
|
||||
"parachain/test-parachains/adder/collator",
|
||||
"parachain/test-parachains/code-upgrader",
|
||||
]
|
||||
|
||||
[badges]
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(¶).map(|l| l.clone())
|
||||
) -> Option<PersistedValidationData> {
|
||||
self.validation_data.get(¶).map(|l| l.persisted.clone())
|
||||
}
|
||||
|
||||
fn full_validation_data(
|
||||
&self,
|
||||
para: ParaId,
|
||||
_assumption: OccupiedCoreAssumption,
|
||||
) -> Option<ValidationData> {
|
||||
self.validation_data.get(¶).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: _,
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>),
|
||||
|
||||
@@ -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 ¶ms.parent_head.0[..])
|
||||
.expect("invalid parent head format.");
|
||||
|
||||
let block_data = BlockData::decode(&mut ¶ms.block_data.0[..])
|
||||
.expect("invalid block data format.");
|
||||
|
||||
let parent_hash = tiny_keccak::keccak256(¶ms.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(),
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -38,6 +38,8 @@ pub mod validity;
|
||||
|
||||
pub mod runtime_api_impl;
|
||||
|
||||
mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
|
||||
@@ -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(¶_id)?,
|
||||
balance: 0,
|
||||
validation_code_hash: BlakeTwo256::hash_of(
|
||||
&Self::current_code(¶_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(¶_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(¶_id),
|
||||
)
|
||||
}
|
||||
|
||||
/// Implementation for the `candidate_pending_availability` function of the runtime API.
|
||||
|
||||
@@ -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(¶_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,
|
||||
})
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user