1717
1818use std:: { fmt:: Debug , str:: FromStr } ;
1919
20+ use bytesize:: ByteSize ;
2021use frame_try_runtime:: UpgradeCheckSelect ;
21- use parity_scale_codec:: { Decode , Encode } ;
22+ use parity_scale_codec:: Encode ;
2223use sc_executor:: sp_wasm_interface:: HostFunctions ;
24+ use sp_core:: { hexdisplay:: HexDisplay , H256 } ;
2325use sp_runtime:: traits:: { Block as BlockT , NumberFor } ;
24- use sp_weights :: Weight ;
26+ use sp_state_machine :: { CompactProof , StorageProof } ;
2527
2628use 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.
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