Skip to content

Commit 57286fc

Browse files
authored
Improved runtime upgrade ref_time and PoV logging (#22)
* better weight logging and snapshot support * minor fixes * fix typo * allow disabling weight warnings * clippy * bump version
1 parent e284781 commit 57286fc

8 files changed

Lines changed: 240 additions & 101 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ members = [
77
]
88

99
[workspace.package]
10-
version = "0.1.0"
10+
version = "0.2.0"
1111
authors = ["Parity Technologies <admin@parity.io>"]
1212
description = "Substrate's programmatic testing framework."
1313
edition = "2021"
@@ -16,6 +16,7 @@ homepage = "https://github.com/paritytech/try-runtime-cli"
1616
repository = "https://github.com/paritytech/try-runtime-cli/"
1717

1818
[workspace.dependencies]
19+
bytesize = { version = "1.2.0" }
1920
assert_cmd = { version = "2.0.10" }
2021
async-trait = { version = "0.1.57" }
2122
clap = { version = "4.0.9" }

core/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ repository.workspace = true
1212
targets = ["x86_64-unknown-linux-gnu"]
1313

1414
[dependencies]
15+
# Crates.io
1516
async-trait = { workspace = true }
17+
bytesize = { workspace = true, features = ["serde"] }
1618
clap = { workspace = true, features = ["derive"], optional = true }
1719
hex = { workspace = true }
1820
log = { workspace = true }

core/src/commands/follow_chain.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
use std::{fmt::Debug, str::FromStr};
1919

20-
use parity_scale_codec::{Decode, Encode};
20+
use parity_scale_codec::Encode;
2121
use sc_executor::sp_wasm_interface::HostFunctions;
2222
use serde::{de::DeserializeOwned, Serialize};
2323
use sp_core::H256;
@@ -180,10 +180,7 @@ where
180180
continue;
181181
}
182182

183-
let (mut changes, encoded_result) = result.expect("checked to be Ok; qed");
184-
185-
let consumed_weight = <sp_weights::Weight as Decode>::decode(&mut &*encoded_result)
186-
.map_err(|e| format!("failed to decode weight: {:?}", e))?;
183+
let (mut changes, _, _) = result.expect("checked to be Ok; qed");
187184

188185
let storage_changes = changes
189186
.drain_storage_changes(
@@ -203,9 +200,8 @@ where
203200

204201
log::info!(
205202
target: LOG_TARGET,
206-
"executed block {}, consumed weight {}, new storage root {:?}",
203+
"executed block {}, new storage root {:?}",
207204
number,
208-
consumed_weight,
209205
state_ext.as_backend().root(),
210206
);
211207
}

core/src/commands/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,22 @@ pub enum Action {
108108
/// tested has remained the same, otherwise block decoding might fail.
109109
FollowChain(follow_chain::Command),
110110

111-
/// Create a new snapshot file.
111+
/// Create snapshot files.
112+
///
113+
/// The `create-snapshot` subcommand facilitates the creation of a snapshot from a node's
114+
/// state. This snapshot can be loaded rapidly into memory from disk, providing an
115+
/// efficient alternative to downloading state from the node for every new command
116+
/// execution.
117+
///
118+
/// **Usage**:
119+
///
120+
/// 1. Create a snapshot from a remote node:
121+
///
122+
/// try-runtime create-snapshot --uri <remote-node-uri> my_state.snap
123+
///
124+
/// 2. Utilize the snapshot with `on-runtime-upgrade`:
125+
///
126+
/// try-runtime --runtime ./path/to/runtime.wasm on-runtime-upgrade snap --path my_state.snap
112127
CreateSnapshot(create_snapshot::Command),
113128
}
114129

core/src/commands/on_runtime_upgrade.rs

Lines changed: 136 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@
1717

1818
use std::{fmt::Debug, str::FromStr};
1919

20+
use bytesize::ByteSize;
2021
use frame_try_runtime::UpgradeCheckSelect;
21-
use parity_scale_codec::{Decode, Encode};
22+
use parity_scale_codec::Encode;
2223
use sc_executor::sp_wasm_interface::HostFunctions;
24+
use sp_core::{hexdisplay::HexDisplay, H256};
2325
use sp_runtime::traits::{Block as BlockT, NumberFor};
24-
use sp_weights::Weight;
26+
use sp_state_machine::{CompactProof, StorageProof};
2527

2628
use crate::{
27-
build_executor, state::State, state_machine_call_with_proof, SharedParams, LOG_TARGET,
29+
build_executor, state::State, state_machine_call_with_proof, RefTimeInfo, SharedParams,
30+
LOG_TARGET,
2831
};
2932

3033
/// Configuration for [`run`].
@@ -48,9 +51,15 @@ pub struct Command {
4851
default_value = "pre-and-post",
4952
default_missing_value = "all",
5053
num_args = 0..=1,
51-
require_equals = true,
52-
verbatim_doc_comment)]
54+
verbatim_doc_comment
55+
)]
5356
pub checks: UpgradeCheckSelect,
57+
58+
/// Whether to assume that the runtime is a relay chain runtime.
59+
///
60+
/// This is used to adjust the behavior of weight measurement warnings.
61+
#[clap(long, default_value = "false", default_missing_value = "true")]
62+
pub no_weight_warnings: bool,
5463
}
5564

5665
// Runs the `on-runtime-upgrade` command.
@@ -69,7 +78,16 @@ where
6978
.to_ext::<Block, HostFns>(&shared, &executor, None, true)
7079
.await?;
7180

72-
let (_, encoded_result) = state_machine_call_with_proof::<HostFns>(
81+
if let State::Live(_) = command.state {
82+
log::info!(
83+
target: LOG_TARGET,
84+
"🚀 Speed up your workflow by using snapshots instead of live state. \
85+
See `try-runtime create-snapshot --help`."
86+
);
87+
}
88+
89+
let pre_root = ext.backend.root();
90+
let (_, proof, ref_time_results) = state_machine_call_with_proof::<HostFns>(
7391
&ext,
7492
&executor,
7593
"TryRuntime_on_runtime_upgrade",
@@ -78,17 +96,121 @@ where
7896
shared.export_proof,
7997
)?;
8098

81-
let (weight, total_weight) = <(Weight, Weight) as Decode>::decode(&mut &*encoded_result)
82-
.map_err(|e| format!("failed to decode weight: {:?}", e))?;
99+
let pov_safety = analyse_pov(proof, *pre_root, command.no_weight_warnings);
100+
let ref_time_safety = analyse_ref_time(ref_time_results, command.no_weight_warnings);
101+
102+
match (pov_safety, ref_time_safety, command.no_weight_warnings) {
103+
(_, _, true) => {
104+
log::info!("✅ TryRuntime_on_runtime_upgrade executed without errors")
105+
}
106+
(WeightSafety::ProbablySafe, WeightSafety::ProbablySafe, _) => {
107+
log::info!(
108+
target: LOG_TARGET,
109+
"✅ TryRuntime_on_runtime_upgrade executed without errors or weight safety \
110+
warnings. Please note this does not guarantee a successful runtime upgrade. \
111+
Always test your runtime upgrade with recent state, and ensure that the weight usage \
112+
of your migrations will not drastically differ between testing and actual on-chain \
113+
execution."
114+
);
115+
}
116+
_ => {
117+
log::warn!(target: LOG_TARGET, "⚠️ TryRuntime_on_runtime_upgrade executed \
118+
successfully but with weight safety warnings.");
119+
}
120+
}
121+
122+
Ok(())
123+
}
124+
125+
enum WeightSafety {
126+
ProbablySafe,
127+
PotentiallyUnsafe,
128+
}
129+
130+
/// The default maximum PoV size in MB.
131+
const DEFAULT_MAX_POV_SIZE: ByteSize = ByteSize::mb(5);
83132

133+
/// The fraction of the total avaliable ref_time or pov size afterwhich a warning should be logged.
134+
const DEFAULT_WARNING_THRESHOLD: f32 = 0.8;
135+
136+
/// Analyse the given ref_times and return if there is a potential weight safety issue.
137+
fn analyse_pov(proof: StorageProof, pre_root: H256, no_weight_warnings: bool) -> WeightSafety {
138+
let encoded_proof_size = proof.encoded_size();
139+
let compact_proof = proof
140+
.clone()
141+
.into_compact_proof::<sp_runtime::traits::BlakeTwo256>(pre_root)
142+
.map_err(|e| {
143+
log::error!(target: LOG_TARGET, "failed to generate compact proof: {:?}", e);
144+
e
145+
})
146+
.unwrap_or(CompactProof {
147+
encoded_nodes: Default::default(),
148+
});
149+
150+
let compact_proof_size = compact_proof.encoded_size();
151+
let compressed_compact_proof = zstd::stream::encode_all(&compact_proof.encode()[..], 0)
152+
.map_err(|e| {
153+
log::error!(
154+
target: LOG_TARGET,
155+
"failed to generate compressed proof: {:?}",
156+
e
157+
);
158+
e
159+
})
160+
.unwrap_or_default();
161+
162+
let proof_nodes = proof.into_nodes();
163+
log::debug!(
164+
target: LOG_TARGET,
165+
"Proof: 0x{}... / {} nodes",
166+
HexDisplay::from(&proof_nodes.iter().flatten().cloned().take(10).collect::<Vec<_>>()),
167+
proof_nodes.len()
168+
);
169+
log::debug!(target: LOG_TARGET, "Encoded proof size: {}", ByteSize(encoded_proof_size as u64));
170+
log::debug!(target: LOG_TARGET, "Compact proof size: {}", ByteSize(compact_proof_size as u64),);
84171
log::info!(
85172
target: LOG_TARGET,
86-
"TryRuntime_on_runtime_upgrade executed without errors. Consumed weight = ({} ps, {} byte), total weight = ({} ps, {} byte) ({:.2} %, {:.2} %).",
87-
weight.ref_time(), weight.proof_size(),
88-
total_weight.ref_time(), total_weight.proof_size(),
89-
(weight.ref_time() as f64 / total_weight.ref_time().max(1) as f64) * 100.0,
90-
(weight.proof_size() as f64 / total_weight.proof_size().max(1) as f64) * 100.0,
173+
"PoV size (zstd-compressed compact proof): {}. For parachains, it's your responsibility \
174+
to verify that a PoV of this size fits within any relaychain constraints.",
175+
ByteSize(compressed_compact_proof.len() as u64),
91176
);
177+
if !no_weight_warnings
178+
&& compressed_compact_proof.len() as f32
179+
> DEFAULT_MAX_POV_SIZE.as_u64() as f32 * DEFAULT_WARNING_THRESHOLD
180+
{
181+
log::warn!(
182+
target: LOG_TARGET,
183+
"A PoV size of {} is significant. Most relay chains usually accept PoVs up to {}. \
184+
Proceed with caution.",
185+
ByteSize(compressed_compact_proof.len() as u64),
186+
DEFAULT_MAX_POV_SIZE,
187+
);
188+
WeightSafety::PotentiallyUnsafe
189+
} else {
190+
WeightSafety::ProbablySafe
191+
}
192+
}
92193

93-
Ok(())
194+
/// Analyse the given ref_times and return if there is a potential weight safety issue.
195+
fn analyse_ref_time(ref_time_results: RefTimeInfo, no_weight_warnings: bool) -> WeightSafety {
196+
let RefTimeInfo { used, max } = ref_time_results;
197+
let (used, max) = (used.as_secs_f32(), max.as_secs_f32());
198+
log::info!(
199+
target: LOG_TARGET,
200+
"Consumed ref_time: {}s ({:.2}% of max {}s)",
201+
used,
202+
used / max * 100.0,
203+
max,
204+
);
205+
if !no_weight_warnings && used >= max * DEFAULT_WARNING_THRESHOLD {
206+
log::warn!(
207+
target: LOG_TARGET,
208+
"Consumed ref_time is >= {}% of the max allowed ref_time. Please ensure the \
209+
migration is not be too computationally expensive to be fit in a single block.",
210+
DEFAULT_WARNING_THRESHOLD * 100.0,
211+
);
212+
WeightSafety::PotentiallyUnsafe
213+
} else {
214+
WeightSafety::ProbablySafe
215+
}
94216
}

0 commit comments

Comments
 (0)