cumulus_pov_validator/
main.rs1use clap::Parser;
20use codec::{Decode, Encode};
21use polkadot_node_primitives::{BlockData, PoV, POV_BOMB_LIMIT};
22use polkadot_parachain_primitives::primitives::ValidationParams;
23use polkadot_primitives::PersistedValidationData;
24use sc_executor::WasmExecutor;
25use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode, WrappedRuntimeCode};
26use std::{fs, path::PathBuf, time::Instant};
27use tracing::level_filters::LevelFilter;
28
29const VALIDATION_CODE_BOMB_LIMIT: usize = 30 * 1024 * 1024;
32
33#[derive(Parser)]
35struct Cli {
36 #[arg(long)]
41 validation_code: PathBuf,
42
43 #[arg(long)]
48 pov: PathBuf,
49}
50
51fn main() -> anyhow::Result<()> {
52 let _ = tracing_subscriber::fmt()
53 .with_env_filter(
54 tracing_subscriber::EnvFilter::from_default_env()
55 .add_directive(LevelFilter::INFO.into()),
56 )
57 .with_writer(std::io::stderr)
58 .try_init();
59
60 let cli = Cli::parse();
61
62 let validation_code = fs::read(&cli.validation_code).map_err(|error| {
63 tracing::error!(%error, path = %cli.validation_code.display(), "Failed to read validation code");
64 anyhow::anyhow!("Failed to read validation code")
65 })?;
66
67 let validation_code =
68 sp_maybe_compressed_blob::decompress(&validation_code, VALIDATION_CODE_BOMB_LIMIT)
69 .map_err(|error| {
70 tracing::error!(%error, "Failed to decompress validation code");
71 anyhow::anyhow!("Failed to decompress validation code")
72 })?;
73
74 let pov_file = fs::read(&cli.pov).map_err(|error| {
75 tracing::error!(%error, path = %cli.pov.display(), "Failed to read PoV");
76 anyhow::anyhow!("Failed to read PoV")
77 })?;
78
79 let executor = WasmExecutor::<sp_io::SubstrateHostFunctions>::builder()
80 .with_allow_missing_host_functions(true)
81 .build();
82
83 let runtime_code = RuntimeCode {
84 code_fetcher: &WrappedRuntimeCode(validation_code.into()),
85 heap_pages: None,
86 hash: vec![1, 2, 3],
89 };
90
91 let _ = executor
93 .call(
94 &mut sp_io::TestExternalities::default().ext(),
95 &runtime_code,
96 "Core_version",
97 &[],
98 CallContext::Offchain,
99 )
100 .0;
101
102 let pov_file_ptr = &mut &pov_file[..];
103 let pov = PoV::decode(pov_file_ptr).map_err(|error| {
104 tracing::error!(%error, "Failed to decode `PoV`");
105 anyhow::anyhow!("Failed to decode `PoV`")
106 })?;
107
108 let pvd = PersistedValidationData::decode(pov_file_ptr).map_err(|error| {
109 tracing::error!(%error, "Failed to `PersistedValidationData`");
110 anyhow::anyhow!("Failed to decode `PersistedValidationData`")
111 })?;
112
113 let pov = sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT).map_err(
114 |error| {
115 tracing::error!(%error, "Failed to decompress `PoV`");
116 anyhow::anyhow!("Failed to decompress `PoV`")
117 },
118 )?;
119
120 let validation_params = ValidationParams {
121 relay_parent_number: pvd.relay_parent_number,
122 relay_parent_storage_root: pvd.relay_parent_storage_root,
123 parent_head: pvd.parent_head,
124 block_data: BlockData(pov.into()),
125 };
126
127 tracing::info!("Starting validation");
128
129 let start = Instant::now();
130
131 let res = executor
132 .call(
133 &mut sp_io::TestExternalities::default().ext(),
134 &runtime_code,
135 "validate_block",
136 &validation_params.encode(),
137 CallContext::Offchain,
138 )
139 .0;
140
141 let duration = start.elapsed();
142
143 match res {
144 Ok(_) => tracing::info!("Validation was successful"),
145 Err(error) => tracing::error!(%error, "Validation failed"),
146 }
147
148 tracing::info!("Validation took {}ms", duration.as_millis());
149
150 Ok(())
151}