From 0fa6dd54f65096069853de2b906efbf3a15c3bf3 Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Thu, 11 Apr 2024 09:55:35 +0200 Subject: [PATCH 1/3] allow only specified network upgrades --- src/build_upgrade.rs | 187 +++++++++++++++++++++++-------------------- src/tests.rs | 100 ++++++++++++++++++++++- src/types.rs | 7 +- 3 files changed, 204 insertions(+), 90 deletions(-) diff --git a/src/build_upgrade.rs b/src/build_upgrade.rs index 677888b..195a4dc 100644 --- a/src/build_upgrade.rs +++ b/src/build_upgrade.rs @@ -8,40 +8,44 @@ use std::path::Path; pub(crate) struct UpgradeArgs { /// Network on which to submit the referendum. `polkadot` or `kusama`. #[clap(long = "network", short)] - network: String, + pub(crate) network: String, + + /// Only include the runtimes explicitly specified. + #[clap(long = "only")] + pub(crate) only: bool, /// The Fellowship release version. Should be semver and correspond to the release published. #[clap(long = "relay-version")] - relay_version: String, + pub(crate) relay_version: Option, /// Optional. The runtime version of Asset Hub to which to upgrade. If not provided, it will use /// the Relay Chain's version. #[clap(long = "asset-hub")] - asset_hub: Option, + pub(crate) asset_hub: Option, /// Optional. The runtime version of Bridge Hub to which to upgrade. If not provided, it will use /// the Relay Chain's version. #[clap(long = "bridge-hub")] - bridge_hub: Option, + pub(crate) bridge_hub: Option, /// Optional. The runtime version of Collectives to which to upgrade. If not provided, it will /// use the Relay Chain's version. #[clap(long = "collectives")] - collectives: Option, + pub(crate) collectives: Option, /// Optional. The runtime version of Encointer to which to upgrade. If not provided, it will use /// the Relay Chain's version. #[clap(long = "encointer")] - encointer: Option, + pub(crate) encointer: Option, /// Name of the file to which to write the output. If not provided, a default will be /// constructed. #[clap(long = "filename")] - filename: Option, + pub(crate) filename: Option, /// Some additional call that you want executed on the Relay Chain along with the upgrade. #[clap(long = "additional")] - additional: Option, + pub(crate) additional: Option, } // The sub-command's "main" function. @@ -66,71 +70,71 @@ pub(crate) async fn build_upgrade(prefs: UpgradeArgs) { write_batch(&upgrade_details, batch); } +fn chain_version(chain: Option, default: Option, only: bool) -> Option { + // if the user specified a version for this particular chain, use it + if let Some(v) = chain { + Some(String::from(v.trim_start_matches('v'))) + } else { + // if the user only wants to upgrade specific chains, and have not specified this one, then + // return None so that it will not be added to the batch of upgrades + if only { + None + // otherwise, use the default version + } else { + assert!(default.is_some(), "no version specified"); + default + } + } +} + // Parse the CLI inputs and return a typed struct with all the details needed. -fn parse_inputs(prefs: UpgradeArgs) -> UpgradeDetails { +pub(crate) fn parse_inputs(prefs: UpgradeArgs) -> UpgradeDetails { let mut networks = Vec::new(); - let relay_version = String::from(prefs.relay_version.trim_start_matches('v')); - let asset_hub_version = if let Some(v) = prefs.asset_hub { - String::from(v.trim_start_matches('v')) - } else { - relay_version.clone() - }; - let bridge_hub_version = if let Some(v) = prefs.bridge_hub { - String::from(v.trim_start_matches('v')) - } else { - relay_version.clone() - }; - let encointer_version = if let Some(v) = prefs.encointer { - String::from(v.trim_start_matches('v')) - } else { - relay_version.clone() - }; - let collectives_version = if let Some(v) = prefs.collectives { - String::from(v.trim_start_matches('v')) - } else { - relay_version.clone() - }; + let only = prefs.only; + + if !only { + assert!( + prefs.relay_version.is_some(), + "relay-version must be specified unless using --only" + ); + } + let relay_version = chain_version(prefs.relay_version, None, only); + let asset_hub_version = chain_version(prefs.asset_hub, relay_version.clone(), only); + let bridge_hub_version = chain_version(prefs.bridge_hub, relay_version.clone(), only); + let encointer_version = chain_version(prefs.encointer, relay_version.clone(), only); + let collectives_version = chain_version(prefs.collectives, relay_version.clone(), only); let relay = match prefs.network.to_ascii_lowercase().as_str() { "polkadot" => { - // Relay must be first! - networks.push(VersionedNetwork { - network: Network::Polkadot, - version: relay_version.clone(), - }); - networks.push(VersionedNetwork { - network: Network::PolkadotAssetHub, - version: asset_hub_version.clone(), - }); - networks.push(VersionedNetwork { - network: Network::PolkadotCollectives, - version: collectives_version.clone(), - }); - networks.push(VersionedNetwork { - network: Network::PolkadotBridgeHub, - version: bridge_hub_version.clone(), - }); - VersionedNetwork { network: Network::Polkadot, version: relay_version.clone() } + if let Some(v) = relay_version.clone() { + networks.push(VersionedNetwork { network: Network::Polkadot, version: v }); + } + if let Some(v) = asset_hub_version.clone() { + networks.push(VersionedNetwork { network: Network::PolkadotAssetHub, version: v }); + } + if let Some(v) = collectives_version.clone() { + networks + .push(VersionedNetwork { network: Network::PolkadotCollectives, version: v }); + } + if let Some(v) = bridge_hub_version.clone() { + networks.push(VersionedNetwork { network: Network::PolkadotBridgeHub, version: v }); + } + Network::Polkadot }, "kusama" => { - // Relay must be first! - networks.push(VersionedNetwork { - network: Network::Kusama, - version: relay_version.clone(), - }); - networks.push(VersionedNetwork { - network: Network::KusamaAssetHub, - version: asset_hub_version.clone(), - }); - networks.push(VersionedNetwork { - network: Network::KusamaBridgeHub, - version: bridge_hub_version.clone(), - }); - networks.push(VersionedNetwork { - network: Network::KusamaEncointer, - version: encointer_version.clone(), - }); - VersionedNetwork { network: Network::Kusama, version: relay_version.clone() } + if let Some(v) = relay_version.clone() { + networks.push(VersionedNetwork { network: Network::Kusama, version: v }); + } + if let Some(v) = asset_hub_version.clone() { + networks.push(VersionedNetwork { network: Network::KusamaAssetHub, version: v }); + } + if let Some(v) = encointer_version.clone() { + networks.push(VersionedNetwork { network: Network::KusamaEncointer, version: v }); + } + if let Some(v) = bridge_hub_version.clone() { + networks.push(VersionedNetwork { network: Network::KusamaBridgeHub, version: v }); + } + Network::Kusama }, _ => panic!("`network` must be `polkadot` or `kusama`"), }; @@ -138,7 +142,7 @@ fn parse_inputs(prefs: UpgradeArgs) -> UpgradeDetails { let additional = match prefs.additional { Some(c) => { let additional_bytes = get_proposal_bytes(c.clone()); - match relay.network { + match relay { Network::Polkadot => Some(CallInfo::from_bytes(&additional_bytes, Network::Polkadot)), Network::Kusama => Some(CallInfo::from_bytes(&additional_bytes, Network::Kusama)), @@ -149,16 +153,21 @@ fn parse_inputs(prefs: UpgradeArgs) -> UpgradeDetails { None => None, }; - let directory = format!("./upgrade-{}-{}/", &prefs.network, &relay_version); + let version = + relay_version.clone().unwrap_or(asset_hub_version.unwrap_or(bridge_hub_version.unwrap_or( + encointer_version.unwrap_or(collectives_version.unwrap_or(String::from("no-version"))), + ))); + + let directory = format!("./upgrade-{}-{}/", &prefs.network, &version); let output_file = if let Some(user_filename) = prefs.filename { format!("{}{}", directory, user_filename) } else { - format!("{}{}-{}.call", directory, prefs.network, relay_version) + format!("{}{}-{}.call", directory, prefs.network, version) }; make_version_directory(directory.as_str()); - UpgradeDetails { relay, networks, directory, output_file, additional } + UpgradeDetails { relay, relay_version, networks, directory, output_file, additional } } // Create a directory into which to place runtime blobs and the final call data. @@ -344,12 +353,14 @@ fn generate_authorize_upgrade_calls(upgrade_details: &UpgradeDetails) -> Vec CallInfo { +// Generate the `system.set_code` call that will upgrade the Relay Chain. +fn generate_relay_upgrade_call(upgrade_details: &UpgradeDetails) -> Option { println!("\nGenerating Relay Chain upgrade call. The runtime hash is logged if you would like to verify it with srtool.\n"); - let runtime_version = semver_to_intver(&upgrade_details.relay.version); - match upgrade_details.relay.network { + if upgrade_details.relay_version.clone().is_none() { + return None + } + let runtime_version = semver_to_intver(&upgrade_details.relay_version.clone().unwrap()); + match upgrade_details.relay { Network::Kusama => { use kusama_relay::runtime_types::frame_system::pallet::Call as SystemCall; @@ -361,8 +372,8 @@ fn generate_relay_upgrade_call(upgrade_details: &UpgradeDetails) -> CallInfo { let runtime_hash = blake2_256(&runtime); println!("Kusama Relay Chain Runtime Hash: 0x{}", hex::encode(runtime_hash)); - CallInfo::from_runtime_call(NetworkRuntimeCall::Kusama(KusamaRuntimeCall::System( - SystemCall::set_code { code: runtime }, + Some(CallInfo::from_runtime_call(NetworkRuntimeCall::Kusama( + KusamaRuntimeCall::System(SystemCall::set_code { code: runtime }), ))) }, Network::Polkadot => { @@ -376,8 +387,8 @@ fn generate_relay_upgrade_call(upgrade_details: &UpgradeDetails) -> CallInfo { let runtime_hash = blake2_256(&runtime); println!("Polkadot Relay Chain Runtime Hash: 0x{}", hex::encode(runtime_hash)); - CallInfo::from_runtime_call(NetworkRuntimeCall::Polkadot(PolkadotRuntimeCall::System( - SystemCall::set_code { code: runtime }, + Some(CallInfo::from_runtime_call(NetworkRuntimeCall::Polkadot( + PolkadotRuntimeCall::System(SystemCall::set_code { code: runtime }), ))) }, _ => panic!("Not a Relay Chain"), @@ -389,11 +400,11 @@ fn generate_relay_upgrade_call(upgrade_details: &UpgradeDetails) -> CallInfo { // referendum. async fn construct_batch( upgrade_details: &UpgradeDetails, - relay_call: CallInfo, + relay_call: Option, para_calls: Vec, ) -> CallInfo { println!("\nBatching calls."); - match upgrade_details.relay.network { + match upgrade_details.relay { Network::Kusama => construct_kusama_batch(relay_call, para_calls, upgrade_details.additional.clone()).await, Network::Polkadot => @@ -405,7 +416,7 @@ async fn construct_batch( // Construct the batch needed on Kusama. async fn construct_kusama_batch( - relay_call: CallInfo, + relay_call: Option, para_calls: Vec, additional: Option, ) -> CallInfo { @@ -437,7 +448,9 @@ async fn construct_kusama_batch( batch_calls.push(a.get_kusama_call().expect("kusama call")) } // Relay set code goes last - batch_calls.push(relay_call.get_kusama_call().expect("kusama call")); + if relay_call.is_some() { + batch_calls.push(relay_call.unwrap().get_kusama_call().expect("kusama call")); + } CallInfo::from_runtime_call(NetworkRuntimeCall::Kusama(KusamaRuntimeCall::Utility( UtilityCall::force_batch { calls: batch_calls }, ))) @@ -445,7 +458,7 @@ async fn construct_kusama_batch( // Construct the batch needed on Polkadot. async fn construct_polkadot_batch( - relay_call: CallInfo, + relay_call: Option, para_calls: Vec, additional: Option, ) -> CallInfo { @@ -476,7 +489,9 @@ async fn construct_polkadot_batch( batch_calls.push(a.get_polkadot_call().expect("polkadot call")) } // Relay set code goes last - batch_calls.push(relay_call.get_polkadot_call().expect("polkadot call")); + if relay_call.is_some() { + batch_calls.push(relay_call.unwrap().get_polkadot_call().expect("polkadot call")); + } CallInfo::from_runtime_call(NetworkRuntimeCall::Polkadot(PolkadotRuntimeCall::Utility( UtilityCall::force_batch { calls: batch_calls }, ))) @@ -593,7 +608,7 @@ fn write_batch(upgrade_details: &UpgradeDetails, batch: CallInfo) { println!("\nSuccess! The call data was written to {}", fname); println!("To submit this as a referendum in OpenGov, run:"); - let network = match upgrade_details.relay.network { + let network = match upgrade_details.relay { Network::Kusama => "kusama", Network::Polkadot => "polkadot", _ => panic!("not a relay network"), diff --git a/src/tests.rs b/src/tests.rs index 32fff36..0bb7d28 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,8 +1,9 @@ use crate::get_proposal_bytes; use crate::polkadot_relay::runtime_types::frame_system::pallet::Call as PolkadotRelaySystemCall; use crate::{ - submit_referendum::generate_calls, CallInfo, CallOrHash, KusamaOpenGovOrigin, Network, - NetworkRuntimeCall, PolkadotOpenGovOrigin, PolkadotRuntimeCall, ProposalDetails, Weight, + build_upgrade, submit_referendum::generate_calls, CallInfo, CallOrHash, KusamaOpenGovOrigin, + Network, NetworkRuntimeCall, PolkadotOpenGovOrigin, PolkadotRuntimeCall, ProposalDetails, + UpgradeArgs, VersionedNetwork, Weight, }; fn polkadot_whitelist_remark_user_input() -> ProposalDetails { @@ -117,6 +118,48 @@ fn limited_length_user_input() -> ProposalDetails { } } +fn upgrade_args_for_only_relay() -> UpgradeArgs { + UpgradeArgs { + network: String::from("polkadot"), + only: true, + relay_version: Some(String::from("v1.2.0")), + asset_hub: None, + bridge_hub: None, + collectives: None, + encointer: None, + filename: None, + additional: None, + } +} + +fn upgrade_args_for_only_asset_hub() -> UpgradeArgs { + UpgradeArgs { + network: String::from("polkadot"), + only: true, + relay_version: None, + asset_hub: Some(String::from("v1.2.0")), + bridge_hub: None, + collectives: None, + encointer: None, + filename: None, + additional: None, + } +} + +fn upgrade_args_for_all() -> UpgradeArgs { + UpgradeArgs { + network: String::from("polkadot"), + only: false, + relay_version: Some(String::from("v1.2.0")), + asset_hub: None, + bridge_hub: None, + collectives: None, + encointer: None, + filename: None, + additional: None, + } +} + #[test] fn call_info_from_bytes_works() { let proposal_details = polkadot_whitelist_remark_user_input(); @@ -401,6 +444,59 @@ async fn it_starts_kusama_root_referenda_correctly() { } } +#[test] +fn only_relay_chain() { + let args = upgrade_args_for_only_relay(); + let details = build_upgrade::parse_inputs(args); + assert_eq!(details.relay, Network::Polkadot); + assert_eq!(details.relay_version, Some(String::from("1.2.0"))); + let mut expected_networks = Vec::new(); + expected_networks + .push(VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }); + assert_eq!(details.networks, expected_networks); + assert!(details.additional.is_none()); +} + +#[test] +fn only_asset_hub() { + let args = upgrade_args_for_only_asset_hub(); + let details = build_upgrade::parse_inputs(args); + assert_eq!(details.relay, Network::Polkadot); + assert_eq!(details.relay_version, None); + let mut expected_networks = Vec::new(); + expected_networks.push(VersionedNetwork { + network: Network::PolkadotAssetHub, + version: String::from("1.2.0"), + }); + assert_eq!(details.networks, expected_networks); + assert!(details.additional.is_none()); +} + +#[test] +fn upgrade_everything_works_with_just_relay_version() { + let args = upgrade_args_for_all(); + let details = build_upgrade::parse_inputs(args); + assert_eq!(details.relay, Network::Polkadot); + assert_eq!(details.relay_version, Some(String::from("1.2.0"))); + let mut expected_networks = Vec::new(); + expected_networks + .push(VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }); + expected_networks.push(VersionedNetwork { + network: Network::PolkadotAssetHub, + version: String::from("1.2.0"), + }); + expected_networks.push(VersionedNetwork { + network: Network::PolkadotCollectives, + version: String::from("1.2.0"), + }); + expected_networks.push(VersionedNetwork { + network: Network::PolkadotBridgeHub, + version: String::from("1.2.0"), + }); + assert_eq!(details.networks, expected_networks); + assert!(details.additional.is_none()); +} + #[test] fn it_creates_constrained_print_output() { let proposal_details = limited_length_user_input(); diff --git a/src/types.rs b/src/types.rs index 0205cb5..fe8bda6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -50,7 +50,7 @@ pub(super) use polkadot_collectives::runtime_types::{ pub mod polkadot_bridge_hub {} pub(super) use polkadot_bridge_hub::runtime_types::bridge_hub_polkadot_runtime::RuntimeCall as PolkadotBridgeHubRuntimeCall; -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub(super) enum Network { Kusama, KusamaAssetHub, @@ -103,7 +103,9 @@ pub(super) struct ProposalDetails { // Info and preferences provided by the user for runtime upgrade construction. pub(super) struct UpgradeDetails { // The Relay Network for this upgrade, Polkadot or Kusama. - pub(super) relay: VersionedNetwork, + pub(super) relay: Network, + // The version of the Relay Chain to which to upgrade. Typically, but not always, the default. + pub(super) relay_version: Option, // All networks to upgrade. pub(super) networks: Vec, // The directory into which to write information needed. @@ -115,6 +117,7 @@ pub(super) struct UpgradeDetails { } // A network and the version to which it will upgrade. +#[derive(Debug, PartialEq)] pub(super) struct VersionedNetwork { // A network identifier. pub(super) network: Network, From 8eaf216b30fa2f7aa4283b955e96bd017aef89f3 Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Thu, 11 Apr 2024 13:45:51 +0200 Subject: [PATCH 2/3] make linter happy --- src/build_upgrade.rs | 13 ++++++------- src/tests.rs | 31 ++++++++++--------------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/build_upgrade.rs b/src/build_upgrade.rs index 195a4dc..efe1609 100644 --- a/src/build_upgrade.rs +++ b/src/build_upgrade.rs @@ -356,9 +356,8 @@ fn generate_authorize_upgrade_calls(upgrade_details: &UpgradeDetails) -> Vec Option { println!("\nGenerating Relay Chain upgrade call. The runtime hash is logged if you would like to verify it with srtool.\n"); - if upgrade_details.relay_version.clone().is_none() { - return None - } + // None if there is no version. + upgrade_details.relay_version.clone()?; let runtime_version = semver_to_intver(&upgrade_details.relay_version.clone().unwrap()); match upgrade_details.relay { Network::Kusama => { @@ -448,8 +447,8 @@ async fn construct_kusama_batch( batch_calls.push(a.get_kusama_call().expect("kusama call")) } // Relay set code goes last - if relay_call.is_some() { - batch_calls.push(relay_call.unwrap().get_kusama_call().expect("kusama call")); + if let Some(rc) = relay_call { + batch_calls.push(rc.get_kusama_call().expect("kusama call")); } CallInfo::from_runtime_call(NetworkRuntimeCall::Kusama(KusamaRuntimeCall::Utility( UtilityCall::force_batch { calls: batch_calls }, @@ -489,8 +488,8 @@ async fn construct_polkadot_batch( batch_calls.push(a.get_polkadot_call().expect("polkadot call")) } // Relay set code goes last - if relay_call.is_some() { - batch_calls.push(relay_call.unwrap().get_polkadot_call().expect("polkadot call")); + if let Some(rc) = relay_call { + batch_calls.push(rc.get_polkadot_call().expect("polkadot call")); } CallInfo::from_runtime_call(NetworkRuntimeCall::Polkadot(PolkadotRuntimeCall::Utility( UtilityCall::force_batch { calls: batch_calls }, diff --git a/src/tests.rs b/src/tests.rs index 0bb7d28..68aa924 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -450,9 +450,8 @@ fn only_relay_chain() { let details = build_upgrade::parse_inputs(args); assert_eq!(details.relay, Network::Polkadot); assert_eq!(details.relay_version, Some(String::from("1.2.0"))); - let mut expected_networks = Vec::new(); - expected_networks - .push(VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }); + let mut expected_networks = + vec![VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }]; assert_eq!(details.networks, expected_networks); assert!(details.additional.is_none()); } @@ -463,11 +462,10 @@ fn only_asset_hub() { let details = build_upgrade::parse_inputs(args); assert_eq!(details.relay, Network::Polkadot); assert_eq!(details.relay_version, None); - let mut expected_networks = Vec::new(); - expected_networks.push(VersionedNetwork { + let expected_networks = vec![VersionedNetwork { network: Network::PolkadotAssetHub, version: String::from("1.2.0"), - }); + }]; assert_eq!(details.networks, expected_networks); assert!(details.additional.is_none()); } @@ -478,21 +476,12 @@ fn upgrade_everything_works_with_just_relay_version() { let details = build_upgrade::parse_inputs(args); assert_eq!(details.relay, Network::Polkadot); assert_eq!(details.relay_version, Some(String::from("1.2.0"))); - let mut expected_networks = Vec::new(); - expected_networks - .push(VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }); - expected_networks.push(VersionedNetwork { - network: Network::PolkadotAssetHub, - version: String::from("1.2.0"), - }); - expected_networks.push(VersionedNetwork { - network: Network::PolkadotCollectives, - version: String::from("1.2.0"), - }); - expected_networks.push(VersionedNetwork { - network: Network::PolkadotBridgeHub, - version: String::from("1.2.0"), - }); + let expected_networks = vec![ + VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }, + VersionedNetwork { network: Network::PolkadotAssetHub, version: String::from("1.2.0") }, + VersionedNetwork { network: Network::PolkadotCollectives, version: String::from("1.2.0") }, + VersionedNetwork { network: Network::PolkadotBridgeHub, version: String::from("1.2.0") }, + ]; assert_eq!(details.networks, expected_networks); assert!(details.additional.is_none()); } From 0f91320db9dac3b57851f584475bfda3741dd1a8 Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Thu, 11 Apr 2024 14:03:33 +0200 Subject: [PATCH 3/3] now happy --- src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests.rs b/src/tests.rs index 68aa924..9f1f4cf 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -450,7 +450,7 @@ fn only_relay_chain() { let details = build_upgrade::parse_inputs(args); assert_eq!(details.relay, Network::Polkadot); assert_eq!(details.relay_version, Some(String::from("1.2.0"))); - let mut expected_networks = + let expected_networks = vec![VersionedNetwork { network: Network::Polkadot, version: String::from("1.2.0") }]; assert_eq!(details.networks, expected_networks); assert!(details.additional.is_none());