Skip to content
Snippets Groups Projects
Commit d22714af authored by Hugo Trentesaux's avatar Hugo Trentesaux Committed by Hugo Trentesaux
Browse files

add predefined secret and save to config

parent ebcae718
No related branches found
No related tags found
No related merge requests found
......@@ -34,6 +34,8 @@ gcli config
gcli config where
# save config to use gdev network for next commands
gcli --network gdev config save
# save config to use Alice predefined secret
gcli -S predefined -s Alice config save
```
## Commands
......
......@@ -31,10 +31,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
let mut data = data.build_client().await?;
match command {
Subcommand::Balance => {
data = data
.build_address()
.fetch_system_properties()
.await?;
data = data.fetch_system_properties().await?;
commands::account::get_balance(data).await?
}
Subcommand::Transfer {
......@@ -42,22 +39,11 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
dest,
keep_alive,
} => {
data = data.build_client().await?;
commands::transfer::transfer(
&data,
amount,
dest,
keep_alive,
)
.await?;
data = data;
commands::transfer::transfer(&data, amount, dest, keep_alive).await?;
}
Subcommand::TransferMultiple { amount, dests } => {
data = data.build_client().await?;
commands::transfer::transfer_multiple(&data,
amount,
dests,
)
.await?;
commands::transfer::transfer_multiple(&data, amount, dests).await?;
}
};
......
......@@ -27,9 +27,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
Subcommand::Repart {
target,
actual_repart,
} => {
commands::net_test::repart(&data, target, actual_repart).await?
}
} => commands::net_test::repart(&data, target, actual_repart).await?,
Subcommand::SpamRoll { actual_repart } => {
commands::net_test::spam_roll(&data, actual_repart).await?
}
......
......@@ -55,22 +55,19 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
.await?
}
Subcommand::Create { target } => {
data = data.build_keypair();
data = data;
commands::identity::create_identity(data.keypair(), data.client(), target).await?;
}
Subcommand::Confirm { name } => {
data = data.build_keypair();
data = data;
commands::identity::confirm_identity(data.keypair(), data.client(), name).await?;
}
Subcommand::Revoke => {
data = data.build_keypair().fetch_idty_index().await?;
data = data.fetch_idty_index().await?;
commands::identity::revoke_identity(data).await?;
}
Subcommand::GenRevocDoc => {
data = data
.build_keypair()
.fetch_idty_index()
.await?;
data = data.fetch_idty_index().await?;
commands::revocation::print_revoc_sig(&data)
}
};
......@@ -136,7 +133,10 @@ pub async fn get_identity(
if let (Some(indexer), Some(account_id), None) = (&indexer, &account_id, &username) {
username = indexer.username_by_pubkey(&account_id.to_string()).await?;
}
println!("Username: {}", username.unwrap_or("<no indexer>".to_string()));
println!(
"Username: {}",
username.unwrap_or("<no indexer>".to_string())
);
Ok(())
}
......
......@@ -3,14 +3,11 @@ use crate::*;
use sp_core::DeriveJunction;
use subxt::ext::sp_runtime::MultiAddress;
pub async fn repart(
data: &Data,
target: u32,
actual_repart: Option<u32>,
) -> anyhow::Result<()> {
pub async fn repart(data: &Data, target: u32, actual_repart: Option<u32>) -> anyhow::Result<()> {
let mut pairs = Vec::new();
for i in actual_repart.unwrap_or_default()..target {
let pair_i = data.keypair()
let pair_i = data
.keypair()
.derive(std::iter::once(DeriveJunction::hard::<u32>(i)), None)
.map_err(|_| anyhow!("Fail to derive //{}", i))?
.0;
......@@ -28,7 +25,8 @@ pub async fn repart(
.await?;
signer.increment_nonce();*/
if let Some(pair_i_account) = data.client()
if let Some(pair_i_account) = data
.client()
.storage()
.fetch(
&runtime::storage().system().account(&pair_i.public().into()),
......@@ -48,7 +46,8 @@ pub async fn spam_roll(data: &Data, actual_repart: usize) -> anyhow::Result<()>
let mut nonce = 0;
let mut pairs = Vec::<(PairSigner<Runtime, Pair>, AccountId)>::with_capacity(actual_repart);
for i in 0..actual_repart {
let pair_i = data.keypair()
let pair_i = data
.keypair()
.derive(std::iter::once(DeriveJunction::hard::<u32>(i as u32)), None)
.map_err(|_| anyhow!("Fail to derive //{}", i))?
.0;
......
......@@ -4,7 +4,8 @@ use std::ops::Deref;
type SessionKeys = [u8; 128];
#[cfg(feature = "gdev")]
type SmithMembershipMetaData = runtime::runtime_types::common_runtime::entities::SmithMembershipMetaData::<SessionKeys>;
type SmithMembershipMetaData =
runtime::runtime_types::common_runtime::entities::SmithMembershipMetaData<SessionKeys>;
/// define smith subcommands
#[derive(Clone, Default, Debug, clap::Parser)]
......@@ -40,7 +41,6 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
let mut data = data.build_client().await?;
match command {
Subcommand::Request { endpoint } => {
data = data.build_keypair();
dbg!(request_smith_membership(&data, endpoint).await)?;
}
Subcommand::GoOnline => {
......@@ -50,14 +50,14 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
go_offline(&data).await?;
}
Subcommand::Cert { to } => {
data = data.build_keypair().fetch_idty_index().await?;
data = data.fetch_idty_index().await?;
cert(&data, to).await?
}
Subcommand::UpdateKeys => {
update_session_keys(&data).await?;
}
Subcommand::SudoSetKey { new_key } => {
data = data.build_keypair().build_client().await?;
data = data.build_client().await?;
commands::sudo::set_key(data.keypair(), data.client(), new_key).await?;
}
Subcommand::ShowExpire { blocks, sessions } => {
......@@ -88,12 +88,11 @@ pub async fn rotate_keys(client: &Client) -> Result<SessionKeys, anyhow::Error>
/// request smith membership
pub async fn request_smith_membership(data: &Data, endpoint: String) -> Result<(), anyhow::Error> {
let session_keys = rotate_keys(data.client()).await?;
let metadata =
SmithMembershipMetaData {
session_keys,
owner_key: data.address(),
p2p_endpoint: endpoint,
};
let metadata = SmithMembershipMetaData {
session_keys,
owner_key: data.address(),
p2p_endpoint: endpoint,
};
let progress = data
.client()
.tx()
......
......@@ -15,7 +15,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
// match subcommand
match command {
Subcommand::Claim => {
data = data.build_keypair();
data = data;
claim_ud(data).await?;
}
};
......
......@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
const APP_NAME: &str = "gcli";
/// defines structure of config file
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
// duniter endpoint
......@@ -11,6 +12,8 @@ pub struct Config {
pub indexer_endpoint: String,
// user address
pub address: Option<AccountId>,
// user secret (substrate format)
pub secret: Option<String>,
}
impl std::default::Default for Config {
......@@ -19,6 +22,7 @@ impl std::default::Default for Config {
duniter_endpoint: String::from(data::LOCAL_DUNITER_ENDPOINT),
indexer_endpoint: String::from(data::LOCAL_INDEXER_ENDPOINT),
address: None,
secret: None,
}
}
}
......@@ -28,11 +32,16 @@ pub fn load_conf() -> Config {
match confy::load(APP_NAME, None) {
Ok(cfg) => cfg,
Err(e) => {
log::warn!("met error while loading config file");
log::error!("{}", e);
log::info!("removing the old conf file and creating a new one");
log::warn!(
"met error while loading config file {}",
confy::get_configuration_file_path(APP_NAME, None)
.unwrap()
.display()
);
log::error!("{:?}", e);
log::info!("using default config instead");
log::info!("call `config save` to overwrite");
let cfg = Config::default();
confy::store(APP_NAME, None, &cfg).expect("unable to write default config");
cfg
}
}
......
use std::str::FromStr;
use crate::*;
use indexer::Indexer;
// consts
pub const SUBSTRATE_MNEMONIC: &str =
"bottom drive obey lake curtain smoke basket hold race lonely fit walk";
pub const LOCAL_DUNITER_ENDPOINT: &str = "ws://localhost:9944";
pub const LOCAL_INDEXER_ENDPOINT: &str = "http://localhost:8080/v1/graphql";
......@@ -67,6 +71,7 @@ impl Data {
..Default::default()
}
.overwrite_from_args()
.build_from_config()
}
// --- getters ---
// the "unwrap" should not fail if data is well prepared
......@@ -80,7 +85,10 @@ impl Data {
self.cfg.address.clone().expect("an address is needed")
}
pub fn keypair(&self) -> Pair {
self.keypair.clone().expect("a keypair is needed")
match self.keypair.clone() {
Some(keypair) => keypair,
None => prompt_secret(self.args.secret_format),
}
}
pub fn idty_index(&self) -> u32 {
self.idty_index.expect("must fetch idty index first")
......@@ -138,45 +146,39 @@ impl Data {
if let Some(indexer_endpoint) = self.args.indexer.clone() {
self.cfg.indexer_endpoint = indexer_endpoint
}
// secret
if self.args.secret.is_some() {
self = self.build_keypair();
// predefined secret
if self.args.secret_format == SecretFormat::Predefined {
match self.args.secret.clone() {
None => {}
Some(derivation) => {
self.cfg.secret = Some(format!("{SUBSTRATE_MNEMONIC}//{derivation}"));
}
};
} else if let Some(secret) = self.args.secret.clone() {
// other secret type
self.cfg.secret = Some(secret);
}
// address
if self.args.address.is_some() {
self = self.build_address();
}
self
}
/// ask user to input an address if needed
pub fn build_address(mut self) -> Self {
if self.cfg.address.is_none() {
self.cfg.address = Some(
get_keys(
self.args.secret_format,
&self.args.address,
&self.args.secret,
NeededKeys::Public,
)
.expect("needed")
.0
.expect("needed"),
);
if let Some(address) = self.args.address.clone() {
self.cfg.address = Some(AccountId::from_str(&address).expect("invalid address"));
}
self
}
/// ask user to input a keypair if needed
pub fn build_keypair(mut self) -> Self {
if self.keypair.is_none() {
let (address, keypair) = get_keys(
self.args.secret_format,
&self.args.address,
&self.args.secret,
NeededKeys::Secret,
)
.expect("needed");
self.cfg.address = address;
self.keypair = keypair;
/// build from config
pub fn build_from_config(mut self) -> Self {
// if a secret is defined, build keypair
if let Some(secret) = self.cfg.secret.clone() {
let (address, keypair) =
addr_and_pair_from_secret(SecretFormat::Predefined, &secret).unwrap();
// if an address is already defined and differs from secret, warns user
if let Some(address_) = self.cfg.address {
if address_ != address {
println!("overwriting address ({address_}) from secret ({address})");
}
}
self.cfg.address = Some(address);
self.cfg.secret = Some(secret);
self.keypair = Some(keypair);
}
self
}
......@@ -187,7 +189,7 @@ impl Data {
GcliError::Duniter(format!(
"could not establish connection with the server {}, due to error {}",
duniter_endpoint,
dbg!(e)
dbg!(e) // needed to get more details TODO fixme
))
})?);
self.genesis_hash = commands::blockchain::fetch_genesis_hash(&self).await?;
......
use crate::*;
use clap::builder::OsStr;
use sp_core::crypto::Ss58Codec;
use std::str::FromStr;
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NeededKeys {
None,
Public,
Secret,
}
// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
// pub enum NeededKeys {
// None,
// Public,
// Secret,
// }
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub enum SecretFormat {
......@@ -19,6 +17,8 @@ pub enum SecretFormat {
/// Substrate secret key or BIP39 mnemonic (optionally followed by derivation path)
#[default]
Substrate,
/// Predefined (Alice, Bob, ...)
Predefined,
}
impl FromStr for SecretFormat {
......@@ -28,6 +28,7 @@ impl FromStr for SecretFormat {
match s {
"seed" => Ok(SecretFormat::Seed),
"substrate" => Ok(SecretFormat::Substrate),
"predefined" => Ok(SecretFormat::Predefined),
_ => Err(std::io::Error::from(std::io::ErrorKind::InvalidInput)),
}
}
......@@ -38,6 +39,7 @@ impl From<SecretFormat> for &'static str {
match val {
SecretFormat::Seed => "seed",
SecretFormat::Substrate => "substrate",
SecretFormat::Predefined => "predefined",
}
}
}
......@@ -48,6 +50,7 @@ impl From<SecretFormat> for OsStr {
}
}
/// get keypair from given string secret
pub fn pair_from_str(secret_format: SecretFormat, secret: &str) -> anyhow::Result<Pair> {
match secret_format {
SecretFormat::Seed => {
......@@ -56,12 +59,24 @@ pub fn pair_from_str(secret_format: SecretFormat, secret: &str) -> anyhow::Resul
let pair = Pair::from_seed(&seed);
Ok(pair)
}
SecretFormat::Substrate => {
// "predefined" replaces secret before
SecretFormat::Substrate | SecretFormat::Predefined => {
Pair::from_string(secret, None).map_err(|_| anyhow!("Invalid secret"))
}
}
}
/// get keypair and address from given secret string
pub fn addr_and_pair_from_secret(
secret_format: SecretFormat,
secret: &str,
) -> anyhow::Result<(AccountId, Pair)> {
let pair = pair_from_str(secret_format, secret)?;
let address = pair.public().into();
Ok((address, pair))
}
/// ask user to input a secret
pub fn prompt_secret(secret_format: SecretFormat) -> Pair {
loop {
match pair_from_str(
......@@ -74,52 +89,53 @@ pub fn prompt_secret(secret_format: SecretFormat) -> Pair {
}
}
pub fn get_keys(
secret_format: SecretFormat,
address: &Option<String>,
secret: &Option<String>,
needed_keys: NeededKeys,
) -> anyhow::Result<(Option<AccountId>, Option<Pair>)> {
// Get from args
let mut account_id = match (address, secret) {
(Some(address), Some(secret)) => {
let pair = pair_from_str(secret_format, secret)?;
let address = AccountId::from_string(address)
.map_err(|_| anyhow!("Invalid address {}", address))?;
assert_eq!(
address,
pair.public().into(),
"Secret and address do not match."
);
return Ok((Some(pair.public().into()), Some(pair)));
}
(None, Some(secret)) => {
let pair = pair_from_str(secret_format, secret)?;
return Ok((Some(pair.public().into()), Some(pair)));
}
(Some(address), None) => {
Some(AccountId::from_str(address).map_err(|_| anyhow!("Invalid address {}", address))?)
}
(None, None) => None,
};
// /// get keys with interactive prompt if needed
// pub fn get_keys(
// secret_format: SecretFormat,
// accout_id: Option<AccountId>,
// secret: &Option<String>,
// needed_keys: NeededKeys,
// ) -> anyhow::Result<(Option<AccountId>, Option<Pair>)> {
// // Get from args
// let mut account_id = match (accout_id, secret) {
// // both are defined, check that they match.
// // if they do not match, use secret
// (Some(accout_id), Some(secret)) => {
// let pair = pair_from_str(secret_format, secret)?;
// let secret_address = pair.public().into();
// if accout_id != secret_address {
// println!("Secret ({secret_address}) and address ({accout_id}) do not match, using secret");
// }
// return Ok((Some(secret_address), Some(pair)));
// }
// // only secret, build both
// (None, Some(secret)) => {
// let pair = pair_from_str(secret_format, secret)?;
// return Ok((Some(pair.public().into()), Some(pair)));
// }
// // only address
// (Some(accout_id), None) => Some(accout_id),
// // none of them
// (None, None) => None,
// };
// Prompt
if needed_keys == NeededKeys::Secret
|| (account_id.is_none() && needed_keys == NeededKeys::Public)
{
loop {
let pair = prompt_secret(secret_format);
// // Prompt
// if needed_keys == NeededKeys::Secret
// || (account_id.is_none() && needed_keys == NeededKeys::Public)
// {
// loop {
// let pair = prompt_secret(secret_format);
if let Some(account_id) = &account_id {
if account_id != &pair.public().into() {
println!("Secret and address do not match.");
}
} else {
account_id = Some(pair.public().into());
return Ok((account_id, Some(pair)));
}
}
}
// if let Some(account_id) = &account_id {
// if account_id != &pair.public().into() {
// println!("Secret and address do not match.");
// }
// } else {
// account_id = Some(pair.public().into());
// return Ok((account_id, Some(pair)));
// }
// }
// }
Ok((account_id, None))
}
// Ok((account_id, None))
// }
......@@ -112,7 +112,9 @@ async fn main() -> Result<(), GcliError> {
Subcommand::Oneshot(subcommand) => {
commands::oneshot::handle_command(data, subcommand).await?
}
Subcommand::Blockchain(subcommand) => commands::blockchain::handle_command(data, subcommand).await?,
Subcommand::Blockchain(subcommand) => {
commands::blockchain::handle_command(data, subcommand).await?
}
Subcommand::Indexer(subcommand) => indexer::handle_command(data, subcommand).await?,
Subcommand::Config(subcommand) => conf::handle_command(data, subcommand)?,
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment