try_runtime_core/commands/on_runtime_upgrade/
mbms.rs1use std::{fmt::Debug, ops::DerefMut, str::FromStr, sync::Arc, time::Duration};
2
3use log::Level;
4use parity_scale_codec::{Codec, Encode};
5use sc_executor::sp_wasm_interface::HostFunctions;
6use sp_core::{Hasher, H256};
7use sp_crypto_hashing::twox_128;
8use sp_runtime::{
9 traits::{Block as BlockT, NumberFor},
10 DeserializeOwned, ExtrinsicInclusionMode,
11};
12use sp_state_machine::TestExternalities;
13use tokio::sync::Mutex;
14
15use crate::{
16 commands::on_runtime_upgrade::Command,
17 common::{
18 empty_block::{
19 inherents::providers::ProviderVariant,
20 production::{core_version, mine_block},
21 },
22 misc_logging::{basti_log, LogLevelGuard},
23 state::{build_executor, state_machine_call_with_proof, RuntimeChecks},
24 },
25 SharedParams, LOG_TARGET,
26};
27
28pub struct MbmChecker<Block, HostFns> {
30 pub command: Command,
31 pub shared: SharedParams,
32 pub runtime_checks: RuntimeChecks,
33 pub _phantom: core::marker::PhantomData<(Block, HostFns)>,
34}
35
36impl<Block, HostFns> MbmChecker<Block, HostFns>
37where
38 Block: BlockT<Hash = H256> + DeserializeOwned,
39 Block::Header: DeserializeOwned,
40 <Block::Hash as FromStr>::Err: Debug,
41 NumberFor<Block>: FromStr,
42 <NumberFor<Block> as FromStr>::Err: Debug,
43 HostFns: HostFunctions,
44{
45 pub async fn check_mbms(&self) -> sc_cli::Result<()> {
46 basti_log(
47 Level::Info,
48 &format!(
49 "🔬 Running Multi-Block-Migrations with checks: {:?}",
50 self.command.checks
51 ),
52 );
53
54 let executor = build_executor(&self.shared);
55 let ext = self
56 .command
57 .state
58 .to_ext::<Block, HostFns>(&self.shared, &executor, None, self.runtime_checks)
59 .await?;
60
61 if core_version::<Block, HostFns>(&ext, &executor)? < 5 {
62 return Err("Your runtime does not support Multi-Block-Migrations. Please disable the check with `--mbms false` or update your runtime.".into());
63 }
64
65 let inner_ext = Arc::new(Mutex::new(ext.inner_ext));
66 let mut parent_header = ext.header.clone();
67 let mut parent_block_building_info = None;
68 let provider_variant =
69 ProviderVariant::Smart(Duration::from_millis(self.command.blocktime));
70 let mut n = 0;
71
72 let mut ext_guard = inner_ext.lock().await;
73 let ext = ext_guard.deref_mut();
74 Self::modify_spec_name(ext).await?;
75 drop(ext_guard);
76
77 loop {
79 let _quiet = LogLevelGuard::new(log::LevelFilter::Info);
80 let (next_block_building_info, next_header, mode) = mine_block::<Block, HostFns>(
81 inner_ext.clone(),
82 &executor,
83 parent_block_building_info,
84 parent_header.clone(),
85 provider_variant,
86 frame_try_runtime::TryStateSelect::None,
87 )
88 .await?;
89
90 parent_block_building_info = Some(next_block_building_info);
91 parent_header = next_header;
92 let first_is_free = n == 0;
94
95 if n > (self.command.mbm_max_blocks + 1) {
96 log::error!(target: LOG_TARGET, "MBM reached its maximum number of allowed blocks after {} blocks. Increase --mbm-max-blocks if you think this is not a bug.", n);
98 return Err("MBM max blocks reached".into());
99 } else if first_is_free || Self::poll_mbms_ongoing(mode, inner_ext.clone()).await {
100 n += 1;
101 log::info!(target: LOG_TARGET, "MBM ongoing for {n} blocks");
102 } else {
103 log::info!(target: LOG_TARGET, "MBM finished after {n} blocks");
104 break;
105 }
106 }
107
108 let mut ext_guard = inner_ext.lock().await;
109 let ext = ext_guard.deref_mut();
110 log::info!(target: LOG_TARGET, "MBM finished. Executing block one more time.");
111
112 let _ = state_machine_call_with_proof::<Block, HostFns>(
113 ext,
114 &mut Default::default(),
115 &executor,
116 "TryRuntime_on_runtime_upgrade",
117 self.command.checks.encode().as_ref(),
118 Default::default(), None,
120 )?;
121
122 Ok(())
123 }
124
125 async fn modify_spec_name<H>(ext: &mut TestExternalities<H>) -> sc_cli::Result<()>
128 where
129 H: Hasher + 'static,
130 H::Out: Codec + Ord,
131 {
132 let key = [twox_128(b"System"), twox_128(b"LastRuntimeUpgrade")].concat();
133 let version = frame_system::LastRuntimeUpgradeInfo {
134 spec_version: 0.into(),
135 spec_name: "definitely-something-different".into(),
136 };
137
138 ext.execute_with(|| {
139 sp_io::storage::set(&key, &version.encode());
140 });
141 ext.commit_all()?;
142
143 Ok(())
144 }
145
146 async fn poll_mbms_ongoing<H>(
148 mode: Option<ExtrinsicInclusionMode>,
149 ext_mutex: std::sync::Arc<Mutex<TestExternalities<H>>>,
150 ) -> bool
151 where
152 H: Hasher + 'static,
153 H::Out: Codec + Ord,
154 {
155 if mode == Some(ExtrinsicInclusionMode::OnlyInherents) {
156 log::info!(target: LOG_TARGET, "Runtime reports OnlyInherents");
157 return true;
158 }
159
160 let mut ext_guard = ext_mutex.lock().await;
161 let ext = ext_guard.deref_mut();
162
163 ext.execute_with(|| {
164 let mbm_in_progress_key =
165 [twox_128(b"MultiBlockMigrations"), twox_128(b"Cursor")].concat();
166 let mbm_in_progress = sp_io::storage::get(&mbm_in_progress_key).unwrap_or_default();
167
168 !mbm_in_progress.is_empty()
169 })
170 }
171}