mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-23 18:55:48 +00:00
Make candidate validation timeouts configurable (#4001)
* pvf: make execution timeout configurable * guide: add timeouts to candidate validation params * add timeouts to candidate validation messages * fmt * port backing to use the backing pvf timeout * port approval-voting to use the execution timeout * port dispute participation to use the correct timeout * fmt * address grumbles & test failure
This commit is contained in:
committed by
GitHub
parent
114e757988
commit
6002865874
@@ -27,7 +27,7 @@ use polkadot_node_primitives::{
|
|||||||
approval::{
|
approval::{
|
||||||
BlockApprovalMeta, DelayTranche, IndirectAssignmentCert, IndirectSignedApprovalVote,
|
BlockApprovalMeta, DelayTranche, IndirectAssignmentCert, IndirectSignedApprovalVote,
|
||||||
},
|
},
|
||||||
SignedDisputeStatement, ValidationResult,
|
SignedDisputeStatement, ValidationResult, APPROVAL_EXECUTION_TIMEOUT,
|
||||||
};
|
};
|
||||||
use polkadot_node_subsystem::{
|
use polkadot_node_subsystem::{
|
||||||
errors::RecoveryError,
|
errors::RecoveryError,
|
||||||
@@ -2235,6 +2235,7 @@ async fn launch_approval(
|
|||||||
validation_code,
|
validation_code,
|
||||||
candidate.descriptor.clone(),
|
candidate.descriptor.clone(),
|
||||||
available_data.pov,
|
available_data.pov,
|
||||||
|
APPROVAL_EXECUTION_TIMEOUT,
|
||||||
val_tx,
|
val_tx,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use futures::{
|
|||||||
|
|
||||||
use polkadot_node_primitives::{
|
use polkadot_node_primitives::{
|
||||||
AvailableData, PoV, SignedDisputeStatement, SignedFullStatement, Statement, ValidationResult,
|
AvailableData, PoV, SignedDisputeStatement, SignedFullStatement, Statement, ValidationResult,
|
||||||
|
BACKING_EXECUTION_TIMEOUT,
|
||||||
};
|
};
|
||||||
use polkadot_node_subsystem_util::{
|
use polkadot_node_subsystem_util::{
|
||||||
self as util,
|
self as util,
|
||||||
@@ -380,7 +381,12 @@ async fn request_candidate_validation(
|
|||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send_message(CandidateValidationMessage::ValidateFromChainState(candidate, pov, tx))
|
.send_message(CandidateValidationMessage::ValidateFromChainState(
|
||||||
|
candidate,
|
||||||
|
pov,
|
||||||
|
BACKING_EXECUTION_TIMEOUT,
|
||||||
|
tx,
|
||||||
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match rx.await {
|
match rx.await {
|
||||||
|
|||||||
@@ -317,9 +317,10 @@ fn backing_second_works() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate.descriptor() => {
|
) if pov == pov && &c == candidate.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(
|
tx.send(Ok(
|
||||||
ValidationResult::Valid(CandidateCommitments {
|
ValidationResult::Valid(CandidateCommitments {
|
||||||
head_data: expected_head_data.clone(),
|
head_data: expected_head_data.clone(),
|
||||||
@@ -476,9 +477,10 @@ fn backing_works() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate_a.descriptor() => {
|
) if pov == pov && &c == candidate_a.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(
|
tx.send(Ok(
|
||||||
ValidationResult::Valid(CandidateCommitments {
|
ValidationResult::Valid(CandidateCommitments {
|
||||||
head_data: expected_head_data.clone(),
|
head_data: expected_head_data.clone(),
|
||||||
@@ -669,9 +671,10 @@ fn backing_works_while_validation_ongoing() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate_a.descriptor() => {
|
) if pov == pov && &c == candidate_a.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
// we never validate the candidate. our local node
|
// we never validate the candidate. our local node
|
||||||
// shouldn't issue any statements.
|
// shouldn't issue any statements.
|
||||||
std::mem::forget(tx);
|
std::mem::forget(tx);
|
||||||
@@ -834,9 +837,10 @@ fn backing_misbehavior_works() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate_a.descriptor() => {
|
) if pov == pov && &c == candidate_a.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(
|
tx.send(Ok(
|
||||||
ValidationResult::Valid(CandidateCommitments {
|
ValidationResult::Valid(CandidateCommitments {
|
||||||
head_data: expected_head_data.clone(),
|
head_data: expected_head_data.clone(),
|
||||||
@@ -980,9 +984,10 @@ fn backing_dont_second_invalid() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate_a.descriptor() => {
|
) if pov == pov && &c == candidate_a.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap();
|
tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1008,9 +1013,10 @@ fn backing_dont_second_invalid() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate_b.descriptor() => {
|
) if pov == pov && &c == candidate_b.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(
|
tx.send(Ok(
|
||||||
ValidationResult::Valid(CandidateCommitments {
|
ValidationResult::Valid(CandidateCommitments {
|
||||||
head_data: expected_head_data.clone(),
|
head_data: expected_head_data.clone(),
|
||||||
@@ -1138,9 +1144,10 @@ fn backing_second_after_first_fails_works() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate.descriptor() => {
|
) if pov == pov && &c == candidate.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap();
|
tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1186,6 +1193,7 @@ fn backing_second_after_first_fails_works() {
|
|||||||
_,
|
_,
|
||||||
pov,
|
pov,
|
||||||
_,
|
_,
|
||||||
|
_,
|
||||||
)
|
)
|
||||||
) => {
|
) => {
|
||||||
assert_eq!(&*pov, &pov_to_second);
|
assert_eq!(&*pov, &pov_to_second);
|
||||||
@@ -1270,9 +1278,10 @@ fn backing_works_after_failed_validation() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
tx,
|
tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate.descriptor() => {
|
) if pov == pov && &c == candidate.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Err(ValidationFailed("Internal test error".into()))).unwrap();
|
tx.send(Err(ValidationFailed("Internal test error".into()))).unwrap();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1646,9 +1655,10 @@ fn retry_works() {
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
c,
|
c,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
_tx,
|
_tx,
|
||||||
)
|
)
|
||||||
) if pov == pov && &c == candidate.descriptor()
|
) if pov == pov && &c == candidate.descriptor() && timeout == BACKING_EXECUTION_TIMEOUT
|
||||||
);
|
);
|
||||||
virtual_overseer
|
virtual_overseer
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ use parity_scale_codec::Encode;
|
|||||||
|
|
||||||
use futures::{channel::oneshot, prelude::*};
|
use futures::{channel::oneshot, prelude::*};
|
||||||
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
@@ -135,6 +135,7 @@ where
|
|||||||
CandidateValidationMessage::ValidateFromChainState(
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
descriptor,
|
descriptor,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
response_sender,
|
response_sender,
|
||||||
) => {
|
) => {
|
||||||
let bg = {
|
let bg = {
|
||||||
@@ -149,6 +150,7 @@ where
|
|||||||
validation_host,
|
validation_host,
|
||||||
descriptor,
|
descriptor,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
&metrics,
|
&metrics,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -165,6 +167,7 @@ where
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
response_sender,
|
response_sender,
|
||||||
) => {
|
) => {
|
||||||
let bg = {
|
let bg = {
|
||||||
@@ -179,6 +182,7 @@ where
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
&metrics,
|
&metrics,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -322,6 +326,7 @@ async fn validate_from_chain_state<Sender>(
|
|||||||
validation_host: ValidationHost,
|
validation_host: ValidationHost,
|
||||||
descriptor: CandidateDescriptor,
|
descriptor: CandidateDescriptor,
|
||||||
pov: Arc<PoV>,
|
pov: Arc<PoV>,
|
||||||
|
timeout: Duration,
|
||||||
metrics: &Metrics,
|
metrics: &Metrics,
|
||||||
) -> Result<ValidationResult, ValidationFailed>
|
) -> Result<ValidationResult, ValidationFailed>
|
||||||
where
|
where
|
||||||
@@ -347,6 +352,7 @@ where
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor.clone(),
|
descriptor.clone(),
|
||||||
pov,
|
pov,
|
||||||
|
timeout,
|
||||||
metrics,
|
metrics,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -377,6 +383,7 @@ async fn validate_candidate_exhaustive(
|
|||||||
validation_code: ValidationCode,
|
validation_code: ValidationCode,
|
||||||
descriptor: CandidateDescriptor,
|
descriptor: CandidateDescriptor,
|
||||||
pov: Arc<PoV>,
|
pov: Arc<PoV>,
|
||||||
|
timeout: Duration,
|
||||||
metrics: &Metrics,
|
metrics: &Metrics,
|
||||||
) -> Result<ValidationResult, ValidationFailed> {
|
) -> Result<ValidationResult, ValidationFailed> {
|
||||||
let _timer = metrics.time_validate_candidate_exhaustive();
|
let _timer = metrics.time_validate_candidate_exhaustive();
|
||||||
@@ -430,7 +437,7 @@ async fn validate_candidate_exhaustive(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let result = validation_backend
|
let result = validation_backend
|
||||||
.validate_candidate(raw_validation_code.to_vec(), params)
|
.validate_candidate(raw_validation_code.to_vec(), timeout, params)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Err(ref e) = result {
|
if let Err(ref e) = result {
|
||||||
@@ -475,6 +482,7 @@ trait ValidationBackend {
|
|||||||
async fn validate_candidate(
|
async fn validate_candidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
raw_validation_code: Vec<u8>,
|
raw_validation_code: Vec<u8>,
|
||||||
|
timeout: Duration,
|
||||||
params: ValidationParams,
|
params: ValidationParams,
|
||||||
) -> Result<WasmValidationResult, ValidationError>;
|
) -> Result<WasmValidationResult, ValidationError>;
|
||||||
}
|
}
|
||||||
@@ -484,12 +492,14 @@ impl ValidationBackend for ValidationHost {
|
|||||||
async fn validate_candidate(
|
async fn validate_candidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
raw_validation_code: Vec<u8>,
|
raw_validation_code: Vec<u8>,
|
||||||
|
timeout: Duration,
|
||||||
params: ValidationParams,
|
params: ValidationParams,
|
||||||
) -> Result<WasmValidationResult, ValidationError> {
|
) -> Result<WasmValidationResult, ValidationError> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
if let Err(err) = self
|
if let Err(err) = self
|
||||||
.execute_pvf(
|
.execute_pvf(
|
||||||
Pvf::from_code(raw_validation_code),
|
Pvf::from_code(raw_validation_code),
|
||||||
|
timeout,
|
||||||
params.encode(),
|
params.encode(),
|
||||||
polkadot_node_core_pvf::Priority::Normal,
|
polkadot_node_core_pvf::Priority::Normal,
|
||||||
tx,
|
tx,
|
||||||
|
|||||||
@@ -341,6 +341,7 @@ impl ValidationBackend for MockValidatorBackend {
|
|||||||
async fn validate_candidate(
|
async fn validate_candidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
_raw_validation_code: Vec<u8>,
|
_raw_validation_code: Vec<u8>,
|
||||||
|
_timeout: Duration,
|
||||||
_params: ValidationParams,
|
_params: ValidationParams,
|
||||||
) -> Result<WasmValidationResult, ValidationError> {
|
) -> Result<WasmValidationResult, ValidationError> {
|
||||||
self.result.clone()
|
self.result.clone()
|
||||||
@@ -384,6 +385,7 @@ fn candidate_validation_ok_is_ok() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -426,6 +428,7 @@ fn candidate_validation_bad_return_is_invalid() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -461,6 +464,7 @@ fn candidate_validation_timeout_is_internal_error() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -495,6 +499,7 @@ fn candidate_validation_code_mismatch_is_invalid() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -534,6 +539,7 @@ fn compressed_code_works() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -573,6 +579,7 @@ fn code_decompression_failure_is_invalid() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -613,6 +620,7 @@ fn pov_decompression_failure_is_invalid() {
|
|||||||
validation_code,
|
validation_code,
|
||||||
descriptor,
|
descriptor,
|
||||||
Arc::new(pov),
|
Arc::new(pov),
|
||||||
|
Duration::from_secs(0),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
use futures::{channel::oneshot, prelude::*};
|
use futures::{channel::oneshot, prelude::*};
|
||||||
|
|
||||||
use polkadot_node_primitives::ValidationResult;
|
use polkadot_node_primitives::{ValidationResult, APPROVAL_EXECUTION_TIMEOUT};
|
||||||
use polkadot_node_subsystem::{
|
use polkadot_node_subsystem::{
|
||||||
errors::{RecoveryError, RuntimeApiError},
|
errors::{RecoveryError, RuntimeApiError},
|
||||||
messages::{
|
messages::{
|
||||||
@@ -269,11 +269,16 @@ async fn participate(
|
|||||||
|
|
||||||
// we issue a request to validate the candidate with the provided exhaustive
|
// we issue a request to validate the candidate with the provided exhaustive
|
||||||
// parameters
|
// parameters
|
||||||
|
//
|
||||||
|
// We use the approval execution timeout because this is intended to
|
||||||
|
// be run outside of backing and therefore should be subject to the
|
||||||
|
// same level of leeway.
|
||||||
ctx.send_message(CandidateValidationMessage::ValidateFromExhaustive(
|
ctx.send_message(CandidateValidationMessage::ValidateFromExhaustive(
|
||||||
available_data.validation_data,
|
available_data.validation_data,
|
||||||
validation_code,
|
validation_code,
|
||||||
candidate_receipt.descriptor.clone(),
|
candidate_receipt.descriptor.clone(),
|
||||||
available_data.pov,
|
available_data.pov,
|
||||||
|
APPROVAL_EXECUTION_TIMEOUT,
|
||||||
validation_tx,
|
validation_tx,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -295,8 +295,8 @@ fn cast_invalid_vote_if_validation_fails_or_is_invalid() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
virtual_overseer.recv().await,
|
virtual_overseer.recv().await,
|
||||||
AllMessages::CandidateValidation(
|
AllMessages::CandidateValidation(
|
||||||
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, tx)
|
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, timeout, tx)
|
||||||
) => {
|
) if timeout == APPROVAL_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::Timeout))).unwrap();
|
tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::Timeout))).unwrap();
|
||||||
},
|
},
|
||||||
"overseer did not receive candidate validation message",
|
"overseer did not receive candidate validation message",
|
||||||
@@ -331,8 +331,8 @@ fn cast_invalid_vote_if_validation_passes_but_commitments_dont_match() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
virtual_overseer.recv().await,
|
virtual_overseer.recv().await,
|
||||||
AllMessages::CandidateValidation(
|
AllMessages::CandidateValidation(
|
||||||
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, tx)
|
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, timeout, tx)
|
||||||
) => {
|
) if timeout == APPROVAL_EXECUTION_TIMEOUT => {
|
||||||
let mut commitments = CandidateCommitments::default();
|
let mut commitments = CandidateCommitments::default();
|
||||||
// this should lead to a commitments hash mismatch
|
// this should lead to a commitments hash mismatch
|
||||||
commitments.processed_downward_messages = 42;
|
commitments.processed_downward_messages = 42;
|
||||||
@@ -371,8 +371,8 @@ fn cast_valid_vote_if_validation_passes() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
virtual_overseer.recv().await,
|
virtual_overseer.recv().await,
|
||||||
AllMessages::CandidateValidation(
|
AllMessages::CandidateValidation(
|
||||||
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, tx)
|
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, timeout, tx)
|
||||||
) => {
|
) if timeout == APPROVAL_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))).unwrap();
|
tx.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))).unwrap();
|
||||||
},
|
},
|
||||||
"overseer did not receive candidate validation message",
|
"overseer did not receive candidate validation message",
|
||||||
@@ -408,8 +408,8 @@ fn failure_to_store_available_data_does_not_preclude_participation() {
|
|||||||
assert_matches!(
|
assert_matches!(
|
||||||
virtual_overseer.recv().await,
|
virtual_overseer.recv().await,
|
||||||
AllMessages::CandidateValidation(
|
AllMessages::CandidateValidation(
|
||||||
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, tx)
|
CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, timeout, tx)
|
||||||
) => {
|
) if timeout == APPROVAL_EXECUTION_TIMEOUT => {
|
||||||
tx.send(Err(ValidationFailed("fail".to_string()))).unwrap();
|
tx.send(Err(ValidationFailed("fail".to_string()))).unwrap();
|
||||||
},
|
},
|
||||||
"overseer did not receive candidate validation message",
|
"overseer did not receive candidate validation message",
|
||||||
|
|||||||
@@ -38,11 +38,17 @@ slotmap::new_key_type! { struct Worker; }
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ToQueue {
|
pub enum ToQueue {
|
||||||
Enqueue { artifact: ArtifactPathId, params: Vec<u8>, result_tx: ResultSender },
|
Enqueue {
|
||||||
|
artifact: ArtifactPathId,
|
||||||
|
execution_timeout: Duration,
|
||||||
|
params: Vec<u8>,
|
||||||
|
result_tx: ResultSender,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExecuteJob {
|
struct ExecuteJob {
|
||||||
artifact: ArtifactPathId,
|
artifact: ArtifactPathId,
|
||||||
|
execution_timeout: Duration,
|
||||||
params: Vec<u8>,
|
params: Vec<u8>,
|
||||||
result_tx: ResultSender,
|
result_tx: ResultSender,
|
||||||
}
|
}
|
||||||
@@ -167,14 +173,14 @@ async fn purge_dead(metrics: &Metrics, workers: &mut Workers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) {
|
fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) {
|
||||||
let ToQueue::Enqueue { artifact, params, result_tx } = to_queue;
|
let ToQueue::Enqueue { artifact, execution_timeout, params, result_tx } = to_queue;
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
target: LOG_TARGET,
|
target: LOG_TARGET,
|
||||||
validation_code_hash = ?artifact.id.code_hash,
|
validation_code_hash = ?artifact.id.code_hash,
|
||||||
"enqueueing an artifact for execution",
|
"enqueueing an artifact for execution",
|
||||||
);
|
);
|
||||||
queue.metrics.execute_enqueued();
|
queue.metrics.execute_enqueued();
|
||||||
let job = ExecuteJob { artifact, params, result_tx };
|
let job = ExecuteJob { artifact, execution_timeout, params, result_tx };
|
||||||
|
|
||||||
if let Some(available) = queue.workers.find_available() {
|
if let Some(available) = queue.workers.find_available() {
|
||||||
assign(queue, available, job);
|
assign(queue, available, job);
|
||||||
@@ -326,7 +332,13 @@ fn assign(queue: &mut Queue, worker: Worker, job: ExecuteJob) {
|
|||||||
queue.mux.push(
|
queue.mux.push(
|
||||||
async move {
|
async move {
|
||||||
let _timer = execution_timer;
|
let _timer = execution_timer;
|
||||||
let outcome = super::worker::start_work(idle, job.artifact.clone(), job.params).await;
|
let outcome = super::worker::start_work(
|
||||||
|
idle,
|
||||||
|
job.artifact.clone(),
|
||||||
|
job.execution_timeout,
|
||||||
|
job.params,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
QueueEvent::StartWork(worker, outcome, job.artifact.id, job.result_tx)
|
QueueEvent::StartWork(worker, outcome, job.artifact.id, job.result_tx)
|
||||||
}
|
}
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ use parity_scale_codec::{Decode, Encode};
|
|||||||
use polkadot_parachain::primitives::ValidationResult;
|
use polkadot_parachain::primitives::ValidationResult;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
const EXECUTION_TIMEOUT: Duration = Duration::from_secs(3);
|
|
||||||
|
|
||||||
/// Spawns a new worker with the given program path that acts as the worker and the spawn timeout.
|
/// Spawns a new worker with the given program path that acts as the worker and the spawn timeout.
|
||||||
///
|
///
|
||||||
/// The program should be able to handle `<program-path> execute-worker <socket-path>` invocation.
|
/// The program should be able to handle `<program-path> execute-worker <socket-path>` invocation.
|
||||||
@@ -69,6 +67,7 @@ pub enum Outcome {
|
|||||||
pub async fn start_work(
|
pub async fn start_work(
|
||||||
worker: IdleWorker,
|
worker: IdleWorker,
|
||||||
artifact: ArtifactPathId,
|
artifact: ArtifactPathId,
|
||||||
|
execution_timeout: Duration,
|
||||||
validation_params: Vec<u8>,
|
validation_params: Vec<u8>,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
let IdleWorker { mut stream, pid } = worker;
|
let IdleWorker { mut stream, pid } = worker;
|
||||||
@@ -108,7 +107,7 @@ pub async fn start_work(
|
|||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ = Delay::new(EXECUTION_TIMEOUT).fuse() => {
|
_ = Delay::new(execution_timeout).fuse() => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: LOG_TARGET,
|
target: LOG_TARGET,
|
||||||
worker_pid = %pid,
|
worker_pid = %pid,
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ pub struct ValidationHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ValidationHost {
|
impl ValidationHost {
|
||||||
/// Execute PVF with the given code, parameters and priority. The result of execution will be sent
|
/// Execute PVF with the given code, execution timeout, parameters and priority.
|
||||||
/// to the provided result sender.
|
/// The result of execution will be sent to the provided result sender.
|
||||||
///
|
///
|
||||||
/// This is async to accommodate the fact a possibility of back-pressure. In the vast majority of
|
/// This is async to accommodate the fact a possibility of back-pressure. In the vast majority of
|
||||||
/// situations this function should return immediately.
|
/// situations this function should return immediately.
|
||||||
@@ -58,12 +58,13 @@ impl ValidationHost {
|
|||||||
pub async fn execute_pvf(
|
pub async fn execute_pvf(
|
||||||
&mut self,
|
&mut self,
|
||||||
pvf: Pvf,
|
pvf: Pvf,
|
||||||
|
execution_timeout: Duration,
|
||||||
params: Vec<u8>,
|
params: Vec<u8>,
|
||||||
priority: Priority,
|
priority: Priority,
|
||||||
result_tx: ResultSender,
|
result_tx: ResultSender,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
self.to_host_tx
|
self.to_host_tx
|
||||||
.send(ToHost::ExecutePvf { pvf, params, priority, result_tx })
|
.send(ToHost::ExecutePvf { pvf, execution_timeout, params, priority, result_tx })
|
||||||
.await
|
.await
|
||||||
.map_err(|_| "the inner loop hung up".to_string())
|
.map_err(|_| "the inner loop hung up".to_string())
|
||||||
}
|
}
|
||||||
@@ -83,8 +84,16 @@ impl ValidationHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ToHost {
|
enum ToHost {
|
||||||
ExecutePvf { pvf: Pvf, params: Vec<u8>, priority: Priority, result_tx: ResultSender },
|
ExecutePvf {
|
||||||
HeadsUp { active_pvfs: Vec<Pvf> },
|
pvf: Pvf,
|
||||||
|
execution_timeout: Duration,
|
||||||
|
params: Vec<u8>,
|
||||||
|
priority: Priority,
|
||||||
|
result_tx: ResultSender,
|
||||||
|
},
|
||||||
|
HeadsUp {
|
||||||
|
active_pvfs: Vec<Pvf>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration for the validation host.
|
/// Configuration for the validation host.
|
||||||
@@ -200,6 +209,7 @@ pub fn start(config: Config, metrics: Metrics) -> (ValidationHost, impl Future<O
|
|||||||
/// to the given result sender.
|
/// to the given result sender.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PendingExecutionRequest {
|
struct PendingExecutionRequest {
|
||||||
|
execution_timeout: Duration,
|
||||||
params: Vec<u8>,
|
params: Vec<u8>,
|
||||||
result_tx: ResultSender,
|
result_tx: ResultSender,
|
||||||
}
|
}
|
||||||
@@ -210,11 +220,18 @@ struct PendingExecutionRequest {
|
|||||||
struct AwaitingPrepare(HashMap<ArtifactId, Vec<PendingExecutionRequest>>);
|
struct AwaitingPrepare(HashMap<ArtifactId, Vec<PendingExecutionRequest>>);
|
||||||
|
|
||||||
impl AwaitingPrepare {
|
impl AwaitingPrepare {
|
||||||
fn add(&mut self, artifact_id: ArtifactId, params: Vec<u8>, result_tx: ResultSender) {
|
fn add(
|
||||||
self.0
|
&mut self,
|
||||||
.entry(artifact_id)
|
artifact_id: ArtifactId,
|
||||||
.or_default()
|
execution_timeout: Duration,
|
||||||
.push(PendingExecutionRequest { params, result_tx });
|
params: Vec<u8>,
|
||||||
|
result_tx: ResultSender,
|
||||||
|
) {
|
||||||
|
self.0.entry(artifact_id).or_default().push(PendingExecutionRequest {
|
||||||
|
execution_timeout,
|
||||||
|
params,
|
||||||
|
result_tx,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(&mut self, artifact_id: &ArtifactId) -> Vec<PendingExecutionRequest> {
|
fn take(&mut self, artifact_id: &ArtifactId) -> Vec<PendingExecutionRequest> {
|
||||||
@@ -360,7 +377,7 @@ async fn handle_to_host(
|
|||||||
to_host: ToHost,
|
to_host: ToHost,
|
||||||
) -> Result<(), Fatal> {
|
) -> Result<(), Fatal> {
|
||||||
match to_host {
|
match to_host {
|
||||||
ToHost::ExecutePvf { pvf, params, priority, result_tx } => {
|
ToHost::ExecutePvf { pvf, execution_timeout, params, priority, result_tx } => {
|
||||||
handle_execute_pvf(
|
handle_execute_pvf(
|
||||||
cache_path,
|
cache_path,
|
||||||
artifacts,
|
artifacts,
|
||||||
@@ -368,6 +385,7 @@ async fn handle_to_host(
|
|||||||
execute_queue,
|
execute_queue,
|
||||||
awaiting_prepare,
|
awaiting_prepare,
|
||||||
pvf,
|
pvf,
|
||||||
|
execution_timeout,
|
||||||
params,
|
params,
|
||||||
priority,
|
priority,
|
||||||
result_tx,
|
result_tx,
|
||||||
@@ -389,6 +407,7 @@ async fn handle_execute_pvf(
|
|||||||
execute_queue: &mut mpsc::Sender<execute::ToQueue>,
|
execute_queue: &mut mpsc::Sender<execute::ToQueue>,
|
||||||
awaiting_prepare: &mut AwaitingPrepare,
|
awaiting_prepare: &mut AwaitingPrepare,
|
||||||
pvf: Pvf,
|
pvf: Pvf,
|
||||||
|
execution_timeout: Duration,
|
||||||
params: Vec<u8>,
|
params: Vec<u8>,
|
||||||
priority: Priority,
|
priority: Priority,
|
||||||
result_tx: ResultSender,
|
result_tx: ResultSender,
|
||||||
@@ -404,6 +423,7 @@ async fn handle_execute_pvf(
|
|||||||
execute_queue,
|
execute_queue,
|
||||||
execute::ToQueue::Enqueue {
|
execute::ToQueue::Enqueue {
|
||||||
artifact: ArtifactPathId::new(artifact_id, cache_path),
|
artifact: ArtifactPathId::new(artifact_id, cache_path),
|
||||||
|
execution_timeout,
|
||||||
params,
|
params,
|
||||||
result_tx,
|
result_tx,
|
||||||
},
|
},
|
||||||
@@ -417,7 +437,7 @@ async fn handle_execute_pvf(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
awaiting_prepare.add(artifact_id, params, result_tx);
|
awaiting_prepare.add(artifact_id, execution_timeout, params, result_tx);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -426,7 +446,7 @@ async fn handle_execute_pvf(
|
|||||||
artifacts.insert_preparing(artifact_id.clone());
|
artifacts.insert_preparing(artifact_id.clone());
|
||||||
send_prepare(prepare_queue, prepare::ToQueue::Enqueue { priority, pvf }).await?;
|
send_prepare(prepare_queue, prepare::ToQueue::Enqueue { priority, pvf }).await?;
|
||||||
|
|
||||||
awaiting_prepare.add(artifact_id, params, result_tx);
|
awaiting_prepare.add(artifact_id, execution_timeout, params, result_tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(())
|
return Ok(())
|
||||||
@@ -499,7 +519,7 @@ async fn handle_prepare_done(
|
|||||||
// It's finally time to dispatch all the execution requests that were waiting for this artifact
|
// It's finally time to dispatch all the execution requests that were waiting for this artifact
|
||||||
// to be prepared.
|
// to be prepared.
|
||||||
let pending_requests = awaiting_prepare.take(&artifact_id);
|
let pending_requests = awaiting_prepare.take(&artifact_id);
|
||||||
for PendingExecutionRequest { params, result_tx } in pending_requests {
|
for PendingExecutionRequest { execution_timeout, params, result_tx } in pending_requests {
|
||||||
if result_tx.is_canceled() {
|
if result_tx.is_canceled() {
|
||||||
// Preparation could've taken quite a bit of time and the requester may be not interested
|
// Preparation could've taken quite a bit of time and the requester may be not interested
|
||||||
// in execution anymore, in which case we just skip the request.
|
// in execution anymore, in which case we just skip the request.
|
||||||
@@ -510,6 +530,7 @@ async fn handle_prepare_done(
|
|||||||
execute_queue,
|
execute_queue,
|
||||||
execute::ToQueue::Enqueue {
|
execute::ToQueue::Enqueue {
|
||||||
artifact: ArtifactPathId::new(artifact_id.clone(), cache_path),
|
artifact: ArtifactPathId::new(artifact_id.clone(), cache_path),
|
||||||
|
execution_timeout,
|
||||||
params,
|
params,
|
||||||
result_tx,
|
result_tx,
|
||||||
},
|
},
|
||||||
@@ -597,6 +618,8 @@ mod tests {
|
|||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
|
|
||||||
|
const TEST_EXECUTION_TIMEOUT: Duration = Duration::from_secs(3);
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn pulse_test() {
|
async fn pulse_test() {
|
||||||
let pulse = pulse_every(Duration::from_millis(100));
|
let pulse = pulse_every(Duration::from_millis(100));
|
||||||
@@ -840,9 +863,15 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (result_tx, _result_rx) = oneshot::channel();
|
let (result_tx, _result_rx) = oneshot::channel();
|
||||||
host.execute_pvf(Pvf::from_discriminator(1), vec![], Priority::Critical, result_tx)
|
host.execute_pvf(
|
||||||
.await
|
Pvf::from_discriminator(1),
|
||||||
.unwrap();
|
TEST_EXECUTION_TIMEOUT,
|
||||||
|
vec![],
|
||||||
|
Priority::Critical,
|
||||||
|
result_tx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
run_until(
|
run_until(
|
||||||
&mut test.run,
|
&mut test.run,
|
||||||
@@ -862,13 +891,20 @@ mod tests {
|
|||||||
let mut host = test.host_handle();
|
let mut host = test.host_handle();
|
||||||
|
|
||||||
let (result_tx, result_rx_pvf_1_1) = oneshot::channel();
|
let (result_tx, result_rx_pvf_1_1) = oneshot::channel();
|
||||||
host.execute_pvf(Pvf::from_discriminator(1), b"pvf1".to_vec(), Priority::Normal, result_tx)
|
host.execute_pvf(
|
||||||
.await
|
Pvf::from_discriminator(1),
|
||||||
.unwrap();
|
TEST_EXECUTION_TIMEOUT,
|
||||||
|
b"pvf1".to_vec(),
|
||||||
|
Priority::Normal,
|
||||||
|
result_tx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (result_tx, result_rx_pvf_1_2) = oneshot::channel();
|
let (result_tx, result_rx_pvf_1_2) = oneshot::channel();
|
||||||
host.execute_pvf(
|
host.execute_pvf(
|
||||||
Pvf::from_discriminator(1),
|
Pvf::from_discriminator(1),
|
||||||
|
TEST_EXECUTION_TIMEOUT,
|
||||||
b"pvf1".to_vec(),
|
b"pvf1".to_vec(),
|
||||||
Priority::Critical,
|
Priority::Critical,
|
||||||
result_tx,
|
result_tx,
|
||||||
@@ -877,9 +913,15 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (result_tx, result_rx_pvf_2) = oneshot::channel();
|
let (result_tx, result_rx_pvf_2) = oneshot::channel();
|
||||||
host.execute_pvf(Pvf::from_discriminator(2), b"pvf2".to_vec(), Priority::Normal, result_tx)
|
host.execute_pvf(
|
||||||
.await
|
Pvf::from_discriminator(2),
|
||||||
.unwrap();
|
TEST_EXECUTION_TIMEOUT,
|
||||||
|
b"pvf2".to_vec(),
|
||||||
|
Priority::Normal,
|
||||||
|
result_tx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
test.poll_and_recv_to_prepare_queue().await,
|
test.poll_and_recv_to_prepare_queue().await,
|
||||||
@@ -947,9 +989,15 @@ mod tests {
|
|||||||
let mut host = test.host_handle();
|
let mut host = test.host_handle();
|
||||||
|
|
||||||
let (result_tx, result_rx) = oneshot::channel();
|
let (result_tx, result_rx) = oneshot::channel();
|
||||||
host.execute_pvf(Pvf::from_discriminator(1), b"pvf1".to_vec(), Priority::Normal, result_tx)
|
host.execute_pvf(
|
||||||
.await
|
Pvf::from_discriminator(1),
|
||||||
.unwrap();
|
TEST_EXECUTION_TIMEOUT,
|
||||||
|
b"pvf1".to_vec(),
|
||||||
|
Priority::Normal,
|
||||||
|
result_tx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
test.poll_and_recv_to_prepare_queue().await,
|
test.poll_and_recv_to_prepare_queue().await,
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ use polkadot_node_core_pvf::{
|
|||||||
start, Config, InvalidCandidate, Metrics, Pvf, ValidationError, ValidationHost,
|
start, Config, InvalidCandidate, Metrics, Pvf, ValidationError, ValidationHost,
|
||||||
};
|
};
|
||||||
use polkadot_parachain::primitives::{BlockData, ValidationParams, ValidationResult};
|
use polkadot_parachain::primitives::{BlockData, ValidationParams, ValidationResult};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
mod adder;
|
mod adder;
|
||||||
mod worker_common;
|
mod worker_common;
|
||||||
|
|
||||||
const PUPPET_EXE: &str = env!("CARGO_BIN_EXE_puppet_worker");
|
const PUPPET_EXE: &str = env!("CARGO_BIN_EXE_puppet_worker");
|
||||||
|
const TEST_EXECUTION_TIMEOUT: Duration = Duration::from_secs(3);
|
||||||
|
|
||||||
struct TestHost {
|
struct TestHost {
|
||||||
_cache_dir: tempfile::TempDir,
|
_cache_dir: tempfile::TempDir,
|
||||||
@@ -64,6 +66,7 @@ impl TestHost {
|
|||||||
.await
|
.await
|
||||||
.execute_pvf(
|
.execute_pvf(
|
||||||
Pvf::from_code(code.into()),
|
Pvf::from_code(code.into()),
|
||||||
|
TEST_EXECUTION_TIMEOUT,
|
||||||
params.encode(),
|
params.encode(),
|
||||||
polkadot_node_core_pvf::Priority::Normal,
|
polkadot_node_core_pvf::Priority::Normal,
|
||||||
result_tx,
|
result_tx,
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ impl Subsystem1 {
|
|||||||
let msg = CandidateValidationMessage::ValidateFromChainState(
|
let msg = CandidateValidationMessage::ValidateFromChainState(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
PoV { block_data: BlockData(Vec::new()) }.into(),
|
PoV { block_data: BlockData(Vec::new()) }.into(),
|
||||||
|
Default::default(),
|
||||||
tx,
|
tx,
|
||||||
);
|
);
|
||||||
ctx.send_message(<Ctx as overseer::SubsystemContext>::AllMessages::from(msg))
|
ctx.send_message(<Ctx as overseer::SubsystemContext>::AllMessages::from(msg))
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ where
|
|||||||
ctx.send_message(CandidateValidationMessage::ValidateFromChainState(
|
ctx.send_message(CandidateValidationMessage::ValidateFromChainState(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
PoV { block_data: BlockData(Vec::new()) }.into(),
|
PoV { block_data: BlockData(Vec::new()) }.into(),
|
||||||
|
Default::default(),
|
||||||
tx,
|
tx,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
@@ -791,7 +792,12 @@ where
|
|||||||
fn test_candidate_validation_msg() -> CandidateValidationMessage {
|
fn test_candidate_validation_msg() -> CandidateValidationMessage {
|
||||||
let (sender, _) = oneshot::channel();
|
let (sender, _) = oneshot::channel();
|
||||||
let pov = Arc::new(PoV { block_data: BlockData(Vec::new()) });
|
let pov = Arc::new(PoV { block_data: BlockData(Vec::new()) });
|
||||||
CandidateValidationMessage::ValidateFromChainState(Default::default(), pov, sender)
|
CandidateValidationMessage::ValidateFromChainState(
|
||||||
|
Default::default(),
|
||||||
|
pov,
|
||||||
|
Default::default(),
|
||||||
|
sender,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_candidate_backing_msg() -> CandidateBackingMessage {
|
fn test_candidate_backing_msg() -> CandidateBackingMessage {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use std::{convert::TryFrom, pin::Pin};
|
use std::{convert::TryFrom, pin::Pin, time::Duration};
|
||||||
|
|
||||||
use bounded_vec::BoundedVec;
|
use bounded_vec::BoundedVec;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
@@ -71,6 +71,17 @@ pub const POV_BOMB_LIMIT: usize = (MAX_POV_SIZE * 4u32) as usize;
|
|||||||
/// Number of sessions we want to consider in disputes.
|
/// Number of sessions we want to consider in disputes.
|
||||||
pub const DISPUTE_WINDOW: SessionIndex = 6;
|
pub const DISPUTE_WINDOW: SessionIndex = 6;
|
||||||
|
|
||||||
|
/// The amount of time to spend on execution during backing.
|
||||||
|
pub const BACKING_EXECUTION_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
|
/// The amount of time to spend on execution during approval or disputes.
|
||||||
|
///
|
||||||
|
/// This is deliberately much longer than the backing execution timeout to
|
||||||
|
/// ensure that in the absence of extremely large disparities between hardware,
|
||||||
|
/// blocks that pass backing are considerd executable by approval checkers or
|
||||||
|
/// dispute participants.
|
||||||
|
pub const APPROVAL_EXECUTION_TIMEOUT: Duration = Duration::from_secs(6);
|
||||||
|
|
||||||
/// The cumulative weight of a block in a fork-choice rule.
|
/// The cumulative weight of a block in a fork-choice rule.
|
||||||
pub type BlockWeight = u32;
|
pub type BlockWeight = u32;
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ use polkadot_statement_table::v1::Misbehavior;
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Network events as transmitted to other subsystems, wrapped in their message types.
|
/// Network events as transmitted to other subsystems, wrapped in their message types.
|
||||||
@@ -114,6 +115,8 @@ pub enum CandidateValidationMessage {
|
|||||||
ValidateFromChainState(
|
ValidateFromChainState(
|
||||||
CandidateDescriptor,
|
CandidateDescriptor,
|
||||||
Arc<PoV>,
|
Arc<PoV>,
|
||||||
|
/// Execution timeout
|
||||||
|
Duration,
|
||||||
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
||||||
),
|
),
|
||||||
/// Validate a candidate with provided, exhaustive parameters for validation.
|
/// Validate a candidate with provided, exhaustive parameters for validation.
|
||||||
@@ -130,6 +133,8 @@ pub enum CandidateValidationMessage {
|
|||||||
ValidationCode,
|
ValidationCode,
|
||||||
CandidateDescriptor,
|
CandidateDescriptor,
|
||||||
Arc<PoV>,
|
Arc<PoV>,
|
||||||
|
/// Execution timeout
|
||||||
|
Duration,
|
||||||
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -138,8 +143,8 @@ impl CandidateValidationMessage {
|
|||||||
/// If the current variant contains the relay parent hash, return it.
|
/// If the current variant contains the relay parent hash, return it.
|
||||||
pub fn relay_parent(&self) -> Option<Hash> {
|
pub fn relay_parent(&self) -> Option<Hash> {
|
||||||
match self {
|
match self {
|
||||||
Self::ValidateFromChainState(_, _, _) => None,
|
Self::ValidateFromChainState(_, _, _, _) => None,
|
||||||
Self::ValidateFromExhaustive(_, _, _, _, _) => None,
|
Self::ValidateFromExhaustive(_, _, _, _, _, _) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`:
|
|||||||
* Load the historical validation code of the parachain by dispatching a `RuntimeApiRequest::ValidationCodeByHash(descriptor.validation_code_hash)` against the state of `block_hash`.
|
* Load the historical validation code of the parachain by dispatching a `RuntimeApiRequest::ValidationCodeByHash(descriptor.validation_code_hash)` against the state of `block_hash`.
|
||||||
* Spawn a background task with a clone of `background_tx`
|
* Spawn a background task with a clone of `background_tx`
|
||||||
* Wait for the available data
|
* Wait for the available data
|
||||||
* Issue a `CandidateValidationMessage::ValidateFromExhaustive` message
|
* Issue a `CandidateValidationMessage::ValidateFromExhaustive` message with `APPROVAL_EXECUTION_TIMEOUT` as the timeout parameter.
|
||||||
* Wait for the result of validation
|
* Wait for the result of validation
|
||||||
* Check that the result of validation, if valid, matches the commitments in the receipt.
|
* Check that the result of validation, if valid, matches the commitments in the receipt.
|
||||||
* If valid, issue a message on `background_tx` detailing the request.
|
* If valid, issue a message on `background_tx` detailing the request.
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ Dispatch a [`AvailabilityDistributionMessage`][ADM]`::FetchPoV{ validator_index,
|
|||||||
### Validate PoV Block
|
### Validate PoV Block
|
||||||
|
|
||||||
Create a `(sender, receiver)` pair.
|
Create a `(sender, receiver)` pair.
|
||||||
Dispatch a `CandidateValidationMessage::Validate(validation function, candidate, pov, sender)` and listen on the receiver for a response.
|
Dispatch a `CandidateValidationMessage::Validate(validation function, candidate, pov, BACKING_EXECUTION_TIMEOUT, sender)` and listen on the receiver for a response.
|
||||||
|
|
||||||
### Distribute Signed Statement
|
### Distribute Signed Statement
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Conclude.
|
|||||||
* If the data is recovered, dispatch a [`RuntimeApiMessage::ValidationCodeByHash`][RuntimeApiMessage] with the parameters `(candidate_receipt.descriptor.validation_code_hash)` at `state.recent_block.hash`.
|
* If the data is recovered, dispatch a [`RuntimeApiMessage::ValidationCodeByHash`][RuntimeApiMessage] with the parameters `(candidate_receipt.descriptor.validation_code_hash)` at `state.recent_block.hash`.
|
||||||
* Dispatch a [`AvailabilityStoreMessage::StoreAvailableData`][AvailabilityStoreMessage] with the data.
|
* Dispatch a [`AvailabilityStoreMessage::StoreAvailableData`][AvailabilityStoreMessage] with the data.
|
||||||
* If the code is not fetched from the chain, return. This should be impossible with correct relay chain configuration, at least if chain synchronization is working correctly.
|
* If the code is not fetched from the chain, return. This should be impossible with correct relay chain configuration, at least if chain synchronization is working correctly.
|
||||||
* Dispatch a [`CandidateValidationMessage::ValidateFromExhaustive`][CandidateValidationMessage] with the available data and the validation code.
|
* Dispatch a [`CandidateValidationMessage::ValidateFromExhaustive`][CandidateValidationMessage] with the available data and the validation code and `APPROVAL_EXECUTION_TIMEOUT` as the timeout parameter.
|
||||||
* If the validation result is `Invalid`, [cast invalid votes](#cast-votes) and return.
|
* If the validation result is `Invalid`, [cast invalid votes](#cast-votes) and return.
|
||||||
* If the validation fails, [cast invalid votes](#cast-votes) and return.
|
* If the validation fails, [cast invalid votes](#cast-votes) and return.
|
||||||
* If the validation succeeds, compute the `CandidateCommitments` based on the validation result and compare against the candidate receipt's `commitments_hash`. If they match, [cast valid votes](#cast-votes) and if not, [cast invalid votes](#cast-votes).
|
* If the validation succeeds, compute the `CandidateCommitments` based on the validation result and compare against the candidate receipt's `commitments_hash`. If they match, [cast valid votes](#cast-votes) and if not, [cast invalid votes](#cast-votes).
|
||||||
|
|||||||
@@ -785,6 +785,9 @@ enum ValidationResult {
|
|||||||
Invalid,
|
Invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BACKING_EXECUTION_TIMEOUT: Duration = 2 seconds;
|
||||||
|
const APPROVAL_EXECUTION_TIMEOUT: Duration = 6 seconds;
|
||||||
|
|
||||||
/// Messages received by the Validation subsystem.
|
/// Messages received by the Validation subsystem.
|
||||||
///
|
///
|
||||||
/// ## Validation Requests
|
/// ## Validation Requests
|
||||||
@@ -807,6 +810,7 @@ pub enum CandidateValidationMessage {
|
|||||||
ValidateFromChainState(
|
ValidateFromChainState(
|
||||||
CandidateDescriptor,
|
CandidateDescriptor,
|
||||||
Arc<PoV>,
|
Arc<PoV>,
|
||||||
|
Duration, // Execution timeout.
|
||||||
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
||||||
),
|
),
|
||||||
/// Validate a candidate with provided, exhaustive parameters for validation.
|
/// Validate a candidate with provided, exhaustive parameters for validation.
|
||||||
@@ -823,6 +827,7 @@ pub enum CandidateValidationMessage {
|
|||||||
ValidationCode,
|
ValidationCode,
|
||||||
CandidateDescriptor,
|
CandidateDescriptor,
|
||||||
Arc<PoV>,
|
Arc<PoV>,
|
||||||
|
Duration, // Execution timeout.
|
||||||
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user