, Error> {
if collation.head_data.0.len() > global_validation.max_head_data_size as _ {
return Err(Error::HeadDataTooLarge(
collation.head_data.0.len(),
global_validation.max_head_data_size as _,
));
}
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,
};
// TODO: remove when ext does not do this.
let fee_schedule = FeeSchedule {
base: 0,
per_byte: 0,
};
let execution_mode = validation_pool
.map(ExecutionMode::Remote)
.unwrap_or(ExecutionMode::Local);
match wasm_executor::validate_candidate(
&validation_code.0,
params,
execution_mode,
spawner,
) {
Ok(result) => {
if result.head_data == collation.head_data {
let fees = validate_upward_messages(
&result.upward_messages,
fee_schedule,
local_validation.balance,
)?;
Ok(ValidatedCandidate {
pov_block,
global_validation,
local_validation,
upward_messages: result.upward_messages,
fees,
processed_downward_messages: result.processed_downward_messages,
})
} else {
Err(Error::HeadDataMismatch)
}
}
Err(e) => Err(e.into()),
}
}
/// Extracts validation parameters from a Polkadot runtime API for a specific parachain.
pub fn validation_params(api: &P, relay_parent: Hash, para_id: ParaId)
-> Result<(LocalValidationData, GlobalValidationData, ValidationCode), Error>
where
P: ProvideRuntimeApi,
P::Api: ParachainHost,
{
let api = api.runtime_api();
let relay_parent = BlockId::hash(relay_parent);
// fetch all necessary data from runtime.
let local_validation = api.local_validation_data(&relay_parent, para_id)?
.ok_or_else(|| Error::InactiveParachain(para_id))?;
let global_validation = api.global_validation_data(&relay_parent)?;
let validation_code = api.parachain_code(&relay_parent, para_id)?
.ok_or_else(|| Error::InactiveParachain(para_id))?;
Ok((local_validation, global_validation, validation_code))
}
/// Does full-pipeline validation of a collation with provided contextual parameters.
pub fn full_output_validation_with_api(
validation_pool: Option<&ValidationPool>,
api: &P,
collation: &CollationInfo,
pov_block: &PoVBlock,
expected_relay_parent: &Hash,
max_block_data_size: Option,
n_validators: usize,
spawner: impl SpawnNamed + 'static,
) -> Result where
P: ProvideRuntimeApi,
P::Api: ParachainHost,
{
let para_id = collation.parachain_index;
let (local_validation, global_validation, validation_code)
= validation_params(&*api, collation.relay_parent, para_id)?;
// put the parameters through the validation pipeline, producing
// erasure chunks.
let encoded_pov = pov_block.encode();
basic_checks(
&collation,
&expected_relay_parent,
max_block_data_size,
&encoded_pov,
)
.and_then(|()| {
let res = validate(
validation_pool,
&collation,
&pov_block,
&local_validation,
&global_validation,
&validation_code,
spawner,
);
match res {
Err(ref err) => log::debug!(
target: "validation",
"Failed to validate PoVBlock for parachain ({}): {:?}",
para_id,
err,
),
Ok(_) => log::debug!(
target: "validation",
"Successfully validated PoVBlock for parachain ({}).",
para_id,
),
}
res
})
.and_then(|validated| validated.full_output(n_validators))
}
#[cfg(test)]
mod tests {
use super::*;
use parachain::primitives::ParachainDispatchOrigin;
fn add_msg(size: usize, msgs: &mut Vec) {
let msg = UpwardMessage { data: vec![0; size], origin: ParachainDispatchOrigin::Parachain };
msgs.push(msg);
}
#[test]
fn validate_upward_messages_works() {
let fee_schedule = FeeSchedule {
base: 1000,
per_byte: 10,
};
let free_balance = 1_000_000;
let mut msgs = Vec::new();
add_msg(100, &mut msgs);
assert_eq!(2000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap());
add_msg(100, &mut msgs);
assert_eq!(4000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap());
add_msg((1_000_000 - 4000 - 1000) / 10, &mut msgs);
assert_eq!(1_000_000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap());
// cannot pay fee.
add_msg(1, &mut msgs);
let err = validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap_err();
assert!(matches!(err, Error::CouldNotCoverFee));
}
}