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