Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: flag sudo to wrap extrinsic #349

Open
wants to merge 10 commits into
base: feat-call-extrinsic-call-data
Choose a base branch
from
44 changes: 41 additions & 3 deletions crates/pop-cli/src/commands/call/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::cli::{self, traits::*};
use anyhow::{anyhow, Result};
use clap::Args;
use pop_parachains::{
construct_extrinsic, encode_call_data, find_extrinsic_by_name, find_pallet_by_name,
parse_chain_metadata, set_up_api, sign_and_submit_extrinsic,
construct_extrinsic, construct_sudo_extrinsic, encode_call_data, find_extrinsic_by_name,
find_pallet_by_name, parse_chain_metadata, set_up_api, sign_and_submit_extrinsic,
sign_and_submit_extrinsic_with_call_data, supported_actions, Action, DynamicPayload, Extrinsic,
OnlineClient, Pallet, Param, SubstrateConfig,
};
Expand Down Expand Up @@ -41,6 +41,9 @@ pub struct CallParachainCommand {
/// Automatically signs and submits the extrinsic without prompting for confirmation.
#[arg(short('y'), long)]
skip_confirm: bool,
/// Authenticates the sudo key and dispatches a function call with `Root` origin.
#[arg(name = "sudo", short = 'S', long)]
sudo: bool,
}

impl CallParachainCommand {
Expand Down Expand Up @@ -191,12 +194,21 @@ impl CallParachainCommand {
cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()?,
};

// Prompt the user to confirm if they want to execute the call with sudo privileges.
if !self.sudo {
self.sudo = cli
.confirm("Would you like to execute this operation with sudo privileges?")
.initial_value(false)
.interact()?;
}

return Ok(CallParachain {
pallet,
extrinsic,
args,
suri,
skip_confirm: self.skip_confirm,
sudo: self.sudo,
});
}
}
Expand Down Expand Up @@ -244,6 +256,7 @@ impl CallParachainCommand {
self.pallet = None;
self.extrinsic = None;
self.args.clear();
self.sudo = false;
}

// Function to check if all required fields are specified
Expand Down Expand Up @@ -278,6 +291,9 @@ struct CallParachain {
suri: String,
/// Whether to automatically sign and submit the extrinsic without prompting for confirmation.
skip_confirm: bool,
/// Whether to authenticate with the sudo key and dispatch the function call with `Root`
/// origin.
sudo: bool,
}

impl CallParachain {
Expand All @@ -299,6 +315,8 @@ impl CallParachain {
return Err(anyhow!("Error: {}", e));
},
};
// If sudo is enabled, wrap the call in a sudo call.
let tx = if self.sudo { construct_sudo_extrinsic(tx).await? } else { tx };
cli.info(format!("Encoded call data: {}", encode_call_data(api, &tx)?))?;
Ok(tx)
}
Expand Down Expand Up @@ -343,6 +361,9 @@ impl CallParachain {
full_message.push_str(&format!(" --args {}", args.join(" ")));
}
full_message.push_str(&format!(" --url {} --suri {}", chain.url, self.suri));
if self.sudo {
full_message.push_str(" --sudo");
}
full_message
}
}
Expand Down Expand Up @@ -517,6 +538,7 @@ mod tests {
suri: Some(DEFAULT_URI.to_string()),
skip_confirm: false,
call_data: None,
sudo: false,
};
let mut cli = MockCli::new().expect_intro("Call a parachain").expect_input(
"Which chain would you like to interact with?",
Expand All @@ -540,11 +562,13 @@ mod tests {
suri: None,
skip_confirm: false,
call_data: None,
sudo: false,
};

let mut cli = MockCli::new()
.expect_intro("Call a parachain")
.expect_input("Signer of the extrinsic:", "//Bob".into())
.expect_confirm("Would you like to execute this operation with sudo privileges?", true)
.expect_input("Enter the value for the parameter: remark", "0x11".into())
.expect_input("Which chain would you like to interact with?", "wss://rpc1.paseo.popnetwork.xyz".into())
.expect_select::<Pallet>(
Expand Down Expand Up @@ -578,7 +602,8 @@ mod tests {
assert_eq!(call_parachain.extrinsic.name, "remark");
assert_eq!(call_parachain.args, ["0x11".to_string()].to_vec());
assert_eq!(call_parachain.suri, "//Bob");
assert_eq!(call_parachain.display(&chain), "pop call parachain --pallet System --extrinsic remark --args \"0x11\" --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Bob");
assert!(call_parachain.sudo);
assert_eq!(call_parachain.display(&chain), "pop call parachain --pallet System --extrinsic remark --args \"0x11\" --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Bob --sudo");
cli.verify()
}

Expand All @@ -594,6 +619,7 @@ mod tests {
suri: None,
skip_confirm: false,
call_data: None,
sudo: false,
};

let mut cli = MockCli::new()
Expand Down Expand Up @@ -634,6 +660,7 @@ mod tests {
assert_eq!(call_parachain.extrinsic.name, "place_order_allow_death");
assert_eq!(call_parachain.args, ["10000".to_string(), "2000".to_string()].to_vec());
assert_eq!(call_parachain.suri, "//Bob");
assert!(!call_config.sudo);
assert_eq!(call_parachain.display(&chain), "pop call parachain --pallet OnDemand --extrinsic place_order_allow_death --args \"10000\" \"2000\" --url wss://polkadot-rpc.publicnode.com/ --suri //Bob");
cli.verify()
}
Expand All @@ -656,6 +683,7 @@ mod tests {
args: vec!["0x11".to_string()].to_vec(),
suri: DEFAULT_URI.to_string(),
skip_confirm: false,
sudo: false,
};
let mut cli = MockCli::new();
// Error, wrong name of the pallet.
Expand All @@ -675,6 +703,11 @@ mod tests {
assert_eq!(tx.call_name(), "remark");
assert_eq!(tx.pallet_name(), "System");

// Prepare extrinsic wrapped in sudo works.
cli = MockCli::new().expect_info("Encoded call data: 0x0f0000000411");
call_config.sudo = true;
call_config.prepare_extrinsic(&api, &mut cli).await?;

cli.verify()
}

Expand All @@ -688,6 +721,7 @@ mod tests {
args: vec!["0x11".to_string()].to_vec(),
suri: DEFAULT_URI.to_string(),
skip_confirm: false,
sudo: false,
};
let mut cli = MockCli::new()
.expect_confirm("Do you want to submit the extrinsic?", false)
Expand All @@ -711,6 +745,7 @@ mod tests {
suri: None,
skip_confirm: false,
call_data: Some("0x00000411".to_string()),
sudo: false,
};
let mut cli = MockCli::new()
.expect_input("Signer of the extrinsic:", "//Bob".into())
Expand All @@ -731,11 +766,13 @@ mod tests {
suri: Some(DEFAULT_URI.to_string()),
skip_confirm: false,
call_data: None,
sudo: true,
};
call_config.reset_for_new_call();
assert_eq!(call_config.pallet, None);
assert_eq!(call_config.extrinsic, None);
assert_eq!(call_config.args.len(), 0);
assert!(!call_config.sudo);
Ok(())
}

Expand All @@ -749,6 +786,7 @@ mod tests {
suri: Some(DEFAULT_URI.to_string()),
skip_confirm: false,
call_data: None,
sudo: false,
};
assert!(!call_config.requires_user_input());
call_config.pallet = None;
Expand Down
6 changes: 3 additions & 3 deletions crates/pop-cli/tests/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{fs, path::Path, process::Command as Cmd};
use strum::VariantArray;
use tokio::time::{sleep, Duration};

/// Test the parachain lifecycle: new, build, up
/// Test the parachain lifecycle: new, build, up, call.
#[tokio::test]
async fn parachain_lifecycle() -> Result<()> {
let temp = tempfile::tempdir().unwrap();
Expand Down Expand Up @@ -126,7 +126,7 @@ name = "collator-01"
assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running");

// pop call parachain --pallet System --extrinsic remark --args "0x11" --url
// ws://127.0.0.1:8833/ --suri //Alice --skip-confirm
// ws://127.0.0.1:8833 --suri //Alice --skip-confirm
Command::cargo_bin("pop")
.unwrap()
.args(&[
Expand All @@ -139,7 +139,7 @@ name = "collator-01"
"--args",
"0x11",
"--url",
"ws://127.0.0.1:8833/",
"ws://127.0.0.1:8833",
"--suri",
"//Alice",
"--skip-confirm",
Expand Down
25 changes: 25 additions & 0 deletions crates/pop-parachains/src/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ pub async fn construct_extrinsic(
Ok(subxt::dynamic::tx(pallet_name, extrinsic_name, parsed_args))
}

/// Constructs a Sudo extrinsic.
///
/// # Arguments
/// * `tx`: The transaction to be executed with sudo privileges.
pub async fn construct_sudo_extrinsic(tx: DynamicPayload) -> Result<DynamicPayload, Error> {
Ok(subxt::dynamic::tx("Sudo", "sudo", [tx.into_value()].to_vec()))
}

/// Signs and submits a given extrinsic to the blockchain.
///
/// # Arguments
Expand Down Expand Up @@ -166,4 +174,21 @@ mod tests {
assert_eq!(encode_call_data(&api, &extrinsic)?, "0x00000411");
Ok(())
}

#[tokio::test]
async fn construct_sudo_extrinsic_works() -> Result<()> {
let extrinsic = construct_extrinsic(
"Balances",
"transfer_allow_death",
vec![
"Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string(),
"100".to_string(),
],
)
.await?;
let sudo_extrinsic = construct_sudo_extrinsic(extrinsic).await?;
assert_eq!(sudo_extrinsic.call_name(), "sudo");
assert_eq!(sudo_extrinsic.pallet_name(), "Sudo");
Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/pop-parachains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub use build::{
generate_plain_chain_spec, generate_raw_chain_spec, is_supported, ChainSpec,
};
pub use call::{
construct_extrinsic, encode_call_data,
construct_extrinsic, construct_sudo_extrinsic, encode_call_data,
metadata::{
action::{supported_actions, Action},
find_extrinsic_by_name, find_pallet_by_name,
Expand Down