Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • clients/rust/gcli-v2s
  • d0p1/gcli-v2s
  • flebon/gcli-v2s
  • zicmama/gcli-v2s
  • Nicolas80/gcli-v2s
5 results
Show changes
Showing
with 849 additions and 393 deletions
[toolchain]
channel = "nightly"
channel = "stable"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>gcli</string>
<key>CFBundleExecutable</key>
<string>osascript</string>
<key>CFBundleIdentifier</key>
<string>com.axiomteam.gcli</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>gcli</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.2.13</string>
<key>CFBundleVersion</key>
<string>20240522.234244</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSAppleScriptEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>gcli script</string>
<key>CFBundleURLSchemes</key>
<array>
<string>file</string>
</array>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>scpt</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>ScriptEditorAppIcon.icns</string>
<key>CFBundleTypeName</key>
<string>AppleScript</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>osascript</string>
<key>CFBundleScriptPath</key>
<string>Contents/Resources/Scripts/install_gcli.scpt</string>
</dict>
</plist>
-- Ask user if they want to install gcli
set userResponse to display dialog "Do you want to install gcli to /usr/local/bin?" buttons {"No", "Yes"} default button "Yes"
if button returned of userResponse is "Yes" then
-- Run shell script to move gcli with administrator privileges
try
do shell script "mv gcli.app/Contents/MacOS/gcli /usr/local/bin" with administrator privileges
display dialog "gcli has been installed to /usr/local/bin and is now available in your PATH."
on error errMsg number errorNumber
if errorNumber is -128 then
display dialog "Installation cancelled."
else
display dialog "An error occurred: " & errMsg
end if
end try
else
display dialog "Installation cancelled."
end if
use crate::{indexer::*, *};
use std::collections::{hash_map, HashMap};
pub struct IdentityCache {
client: Client,
identities: HashMap<IdtyId, String>,
indexer: Option<Indexer>,
}
impl IdentityCache {
pub fn new(client: Client, indexer: Option<Indexer>) -> Self {
Self {
client,
identities: HashMap::new(),
indexer,
}
}
pub async fn fetch_identity(
&mut self,
identity_id: IdtyId,
parent_hash: sp_core::H256,
) -> anyhow::Result<String> {
Ok(match self.identities.entry(identity_id) {
hash_map::Entry::Occupied(entry) => entry.get().clone(),
hash_map::Entry::Vacant(entry) => entry
.insert({
let pubkey = self
.client
.storage()
.at(parent_hash)
.fetch(&runtime::storage().identity().identities(identity_id))
.await?
.ok_or_else(|| anyhow!("Identity {} not found", identity_id))?
.owner_key
.to_string();
format!(
"“ {} ”",
if let Some(indexer) = &self.indexer {
if let Ok(Some(username)) = indexer.username_by_pubkey(&pubkey).await {
username
} else {
pubkey
}
} else {
pubkey
}
)
})
.clone(),
})
}
}
......@@ -15,3 +15,4 @@ pub mod smith;
pub mod sudo;
pub mod transfer;
pub mod ud;
pub mod vault;
......@@ -11,6 +11,7 @@ pub enum Subcommand {
/// Amount to transfer
amount: u64,
/// Destination address
#[clap(value_name = "ADDRESS")]
dest: AccountId,
/// Prevent from going below account existential deposit
#[clap(short = 'k', long = "keep-alive")]
......@@ -20,11 +21,12 @@ pub enum Subcommand {
is_ud: bool,
},
/// Transfer the same amount for each space-separated address.
/// If an address appears mutiple times, it will get multiple times the same amount
/// If an address appears multiple times, it will get multiple times the same amount
TransferMultiple {
/// Amount given to each destination address
amount: u64,
/// List of target addresses
#[clap(value_name = "ADDRESSES")]
dests: Vec<AccountId>,
},
/// Unlink the account from the linked identity
......@@ -56,7 +58,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
}
/// get balance
pub async fn get_balance(data: Data) -> Result<(), anyhow::Error> {
pub async fn get_balance(data: Data) -> Result<(), subxt::Error> {
let account_id = data.address();
let account_info = get_account_info(data.client(), &account_id).await?;
if let Some(account_info) = account_info {
......@@ -87,7 +89,7 @@ pub async fn get_account_info(
pub async fn unlink_account(data: &Data) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::account::events::AccountUnlinked,
Payload<runtime::account::calls::types::UnlinkIdentity>,
StaticPayload<runtime::account::calls::types::UnlinkIdentity>,
>(data, &runtime::tx().account().unlink_identity())
.await
}
......@@ -39,17 +39,11 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
commands::runtime::runtime_info(data).await;
}
Subcommand::CurrentBlock => {
println!(
"current block on {}: {}",
data.cfg.duniter_endpoint,
data.client()
.storage()
.at_latest()
.await?
.fetch(&runtime::storage().system().number())
.await?
.unwrap()
);
let finalized_number = fetch_finalized_number(&data).await?;
let current_number = fetch_latest_number_and_hash(&data).await?.0;
println!("on {}", data.cfg.duniter_endpoint);
println!("finalized block\t{}", finalized_number);
println!("current block\t{}", current_number);
}
Subcommand::CreateBlock => {
todo!()
......@@ -63,7 +57,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
}
/// get genesis hash
pub async fn fetch_genesis_hash(data: &Data) -> Result<Hash, anyhow::Error> {
pub async fn fetch_genesis_hash(data: &Data) -> Result<Hash, subxt::Error> {
Ok(data
.client()
.storage()
......@@ -73,3 +67,57 @@ pub async fn fetch_genesis_hash(data: &Data) -> Result<Hash, anyhow::Error> {
.await?
.unwrap())
}
/// get finalized number
pub async fn fetch_finalized_number(data: &Data) -> Result<BlockNumber, subxt::Error> {
Ok(data
.client()
.storage()
.at_latest()
.await?
.fetch(&runtime::storage().system().number())
.await?
.unwrap())
}
/// get finalized hash and number (require legacy)
pub async fn fetch_finalized_number_and_hash(
data: &Data,
) -> Result<(BlockNumber, Hash), subxt::Error> {
let hash = data
.legacy_rpc_methods()
.await
.chain_get_finalized_head()
.await?;
let number = data
.legacy_rpc_methods()
.await
.chain_get_block(Some(hash))
.await?
.unwrap()
.block
.header
.number;
Ok((number, hash))
}
/// get latest hash and number
pub async fn fetch_latest_number_and_hash(
data: &Data,
) -> Result<(BlockNumber, Hash), subxt::Error> {
let number = data
.legacy_rpc_methods()
.await
.chain_get_header(None)
.await?
.unwrap()
.number;
let hash = data
.legacy_rpc_methods()
.await
.chain_get_block_hash(Some(number.into()))
.await?
.unwrap();
Ok((number, hash))
}
use crate::*;
/// submit a certification and track progress
pub async fn certify(data: &Data, receiver: IdtyId) -> Result<(), anyhow::Error> {
let progress = submit_call(data, &runtime::tx().certification().add_cert(receiver)).await?;
if data.args.no_wait {
return Ok(());
}
let events = track_progress(progress).await?;
// look for the expected event
look_event::<runtime::certification::events::CertAdded>(data, &events)?;
look_event::<runtime::certification::events::CertRenewed>(data, &events)?;
Ok(())
pub async fn certify(data: &Data, target: IdtyId) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::certification::events::CertAdded,
StaticPayload<runtime::certification::calls::types::AddCert>,
>(data, &runtime::tx().certification().add_cert(target))
.await
}
/// renew certification
pub async fn renew(data: &Data, target: IdtyId) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::certification::events::CertRenewed,
StaticPayload<runtime::certification::calls::types::RenewCert>,
>(data, &runtime::tx().certification().renew_cert(target))
.await
}
use crate::*;
use crate::data::Data;
use crate::keys;
use crate::keys::KeyPair;
use crate::runtime_config::AccountId;
use crate::utils::GcliError;
use bs58;
use sp_core::{ed25519, Pair};
use std::str::FromStr;
/// define cesium subcommands
#[derive(Clone, Default, Debug, clap::Parser)]
......@@ -29,56 +35,113 @@ pub async fn handle_command(_data: Data, command: Subcommand) -> Result<(), Gcli
.unwrap()
};
println!("Pubkey (hex): 0x{}", hex::encode(raw_pubkey));
let address: AccountId = sp_core::ed25519::Public(raw_pubkey).into();
let address: AccountId = sp_core::ed25519::Public::from_raw(raw_pubkey).into();
println!("Address (SS58): {}", address);
}
Subcommand::Prompt => {
let keypair = prompt_secret_cesium();
println!("Pubkey: {}", bs58::encode(keypair.pkey).into_string());
let address: AccountId = keypair.pkey.into();
let pair = keys::prompt_secret_cesium();
println!(
"Pubkey: {}",
compute_g1v1_public_key_from_ed25519_pair(&pair)
);
let address: AccountId = pair.public().into();
println!("Address: {}", address);
}
}
Ok(())
}
pub struct CesiumSigner<T: subxt::Config> {
account_id: T::AccountId,
keypair: nacl::sign::Keypair,
}
impl<T> CesiumSigner<T>
where
T: subxt::Config,
T::AccountId: From<[u8; 32]>,
{
pub fn new(keypair: nacl::sign::Keypair) -> Self {
Self {
account_id: T::AccountId::from(keypair.pkey),
keypair,
}
/// Computes G1v1 public key from a KeyPair of type sp_core::ed25519::Pair - fails otherwise
pub fn compute_g1v1_public_key(key_pair: &KeyPair) -> Result<String, GcliError> {
match key_pair {
KeyPair::Sr25519(_) => Err(GcliError::Logic(
"key pair is not of type ed25519".to_string(),
)),
KeyPair::Ed25519(key_pair) => Ok(compute_g1v1_public_key_from_ed25519_pair(key_pair)),
}
}
impl<T> subxt::tx::Signer<T> for CesiumSigner<T>
where
T: subxt::Config,
T::Address: From<T::AccountId>,
T::Signature: From<sp_core::ed25519::Signature>,
{
fn account_id(&self) -> T::AccountId {
self.account_id.clone()
}
fn address(&self) -> T::Address {
self.account_id.clone().into()
}
/// Computes G1v1 public key from an ed25519 Pair
pub fn compute_g1v1_public_key_from_ed25519_pair(ed25519_key_pair: &ed25519::Pair) -> String {
compute_g1v1_public_key_from_ed25519_public(&ed25519_key_pair.public())
}
/// Computes G1v1 public key from an ed25519 Public
pub fn compute_g1v1_public_key_from_ed25519_public(ed25519_public: &ed25519::Public) -> String {
bs58::encode(ed25519_public).into_string()
}
/// Computes G1v1 public key from an ed25519 AccountId
pub fn compute_g1v1_public_key_from_ed25519_account_id(ed25519_account_id: &AccountId) -> String {
let ed25519_public: ed25519::Public = ed25519::Public::from(ed25519_account_id.0);
bs58::encode(ed25519_public).into_string()
}
/// Computes G1v1 public key from an ed25519 SS58 Address
#[allow(unused)]
pub fn compute_g1v1_public_key_from_ed25519_ss58_address(
ed25519_ss58_address: &str,
) -> Result<String, GcliError> {
Ok(compute_g1v1_public_key_from_ed25519_account_id(
&AccountId::from_str(ed25519_ss58_address).map_err(|e| GcliError::Input(e.to_string()))?,
))
}
// Unit tests
#[cfg(test)]
mod tests {
use super::*;
/// Test data:
/// cesium_id = "test_cesium_id"
/// cesium_pwd = "test_cesium_pwd"
///
/// G1v1 base58 public key: 86pW1doyJPVH3jeDPZNQa1UZFBo5zcdvHERcaeE758W7
///
/// ```
/// subkey inspect --scheme ed25519
/// URI:
/// Secret Key URI `0x2101d2bc68de9ad149c06293bfe489c8608de576c88927aa5439a81be17aae84` is account:
/// Network ID: substrate
/// Secret seed: 0x2101d2bc68de9ad149c06293bfe489c8608de576c88927aa5439a81be17aae84
/// Public key (hex): 0x697f6bd16ddebf142384e503fd3f3efc39fe5c7be7c693bd98d982403bb6eb74
/// Account ID: 0x697f6bd16ddebf142384e503fd3f3efc39fe5c7be7c693bd98d982403bb6eb74
/// Public key (SS58): 5ET2jhgJFoNQUpgfdSkdwftK8DKWdqZ1FKm5GKWdPfMWhPr4
/// SS58 Address: 5ET2jhgJFoNQUpgfdSkdwftK8DKWdqZ1FKm5GKWdPfMWhPr4
/// ```
#[test]
fn test_compute_g1v1_public_key() {
let expected_base58_public_key = "86pW1doyJPVH3jeDPZNQa1UZFBo5zcdvHERcaeE758W7";
let ss58_address = "5ET2jhgJFoNQUpgfdSkdwftK8DKWdqZ1FKm5GKWdPfMWhPr4";
assert_eq!(
expected_base58_public_key,
compute_g1v1_public_key_from_ed25519_ss58_address(ss58_address).unwrap()
);
let account_id = AccountId::from_str(ss58_address).unwrap();
assert_eq!(
expected_base58_public_key,
compute_g1v1_public_key_from_ed25519_account_id(&account_id)
);
let ed25519_public = ed25519::Public::from(account_id.0);
assert_eq!(
expected_base58_public_key,
compute_g1v1_public_key_from_ed25519_public(&ed25519_public)
);
let cesium_id = "test_cesium_id".to_string();
let cesium_pwd = "test_cesium_pwd".to_string();
let seed = keys::seed_from_cesium(&cesium_id, &cesium_pwd);
let ed25519_pair_from_seed = ed25519::Pair::from_seed(&seed);
fn sign(&self, payload: &[u8]) -> T::Signature {
sp_core::ed25519::Signature(
nacl::sign::signature(payload, &self.keypair.skey)
.unwrap()
.try_into()
.expect("could not read signature"),
)
.into()
assert_eq!(
expected_base58_public_key,
compute_g1v1_public_key_from_ed25519_pair(&ed25519_pair_from_seed)
);
}
}
use crate::*;
use anyhow::Result;
/// define technical committee subcommands
#[derive(Clone, Default, Debug, clap::Parser)]
pub enum Subcommand {
......@@ -25,7 +23,7 @@ pub enum Subcommand {
/// handle technical committee commands
pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
let data = data.build_client().await?;
let data = data.build_client().await?.build_indexer().await?;
match command {
Subcommand::Members => technical_committee_members(&data).await?,
Subcommand::Propose { hex } => technical_committee_propose(&data, &hex).await?,
......@@ -48,47 +46,45 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
}
/// list technical committee members
pub async fn technical_committee_members(data: &Data) -> Result<()> {
pub async fn technical_committee_members(data: &Data) -> Result<(), subxt::Error> {
let client = data.client();
let indexer = data.indexer.clone();
let indexer = &data.indexer;
let parent_hash = client
for account_id in client
.storage()
.at_latest()
.await?
.fetch(&runtime::storage().system().parent_hash())
.await?
.unwrap();
for account_id in client
.storage()
.at(parent_hash)
.fetch(&runtime::storage().technical_committee().members())
.await?
.unwrap_or_default()
{
println!(
"{}",
if let Some(indexer) = &indexer {
indexer
.username_by_pubkey(&account_id.to_string())
.await
.ok()
.flatten()
if let Some(indexer) = indexer {
// indexer is set, we can get the name
let name = indexer.username_by_pubkey(&account_id.to_string()).await;
if name.is_some() {
name
} else {
indexer
.wasname_by_pubkey(&account_id.to_string())
.await
.map(|name| format!("{name}\t(old account)"))
}
} else {
// indexer is not set, we can get the idty index by accountid
client
.storage()
.at(parent_hash)
.at_latest()
.await?
.fetch(&runtime::storage().identity().identity_index_of(&account_id))
.await
.ok()
.flatten()
.await?
.map(|identity_id| format!("{identity_id}"))
}
// no idty found, display account_id
.unwrap_or_else(|| account_id.to_string(),)
);
}
Ok(())
}
......@@ -96,7 +92,7 @@ pub async fn technical_committee_members(data: &Data) -> Result<()> {
// TODO:
// * better formatting (format pubkeys to SS58 and add usernames)
// * display proposals indices
pub async fn technical_committee_proposals(client: &Client) -> Result<()> {
pub async fn technical_committee_proposals(client: &Client) -> Result<(), subxt::Error> {
let parent_hash = client
.storage()
.at_latest()
......@@ -110,12 +106,11 @@ pub async fn technical_committee_proposals(client: &Client) -> Result<()> {
.at(parent_hash)
.iter(runtime::storage().technical_committee().proposal_of_iter())
.await?;
while let Some(Ok((proposal_hash, proposal))) = proposals_iter.next().await {
println!("{}", hex::encode(&proposal_hash[32..64]));
println!("{proposal:#?}");
while let Some(Ok(item)) = proposals_iter.next().await {
println!("{}", hex::encode(&item.key_bytes[32..64]));
println!("{:#?}", item.value);
println!();
}
Ok(())
}
......@@ -128,7 +123,7 @@ pub async fn technical_committee_vote(
) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::technical_committee::events::Voted,
Payload<runtime::technical_committee::calls::types::Vote>,
StaticPayload<runtime::technical_committee::calls::types::Vote>,
>(
data,
&runtime::tx()
......@@ -147,7 +142,7 @@ pub async fn technical_committee_propose(data: &Data, proposal: &str) -> Result<
submit_call_and_look_event::<
runtime::technical_committee::events::Proposed,
Payload<runtime::technical_committee::calls::types::Propose>,
StaticPayload<runtime::technical_committee::calls::types::Propose>,
>(data, &payload)
.await
}
......@@ -4,7 +4,7 @@ use crate::*;
pub async fn request_distance_evaluation(data: &Data) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::distance::events::EvaluationRequested,
Payload<runtime::distance::calls::types::RequestDistanceEvaluation>,
StaticPayload<runtime::distance::calls::types::RequestDistanceEvaluation>,
>(
data,
&runtime::tx().distance().request_distance_evaluation(),
......@@ -19,7 +19,7 @@ pub async fn request_distance_evaluation_for(
) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::distance::events::EvaluationRequested,
Payload<runtime::distance::calls::types::RequestDistanceEvaluationFor>,
StaticPayload<runtime::distance::calls::types::RequestDistanceEvaluationFor>,
>(
data,
&runtime::tx()
......
use crate::*;
use crate::{indexer::*, *};
use futures::join;
use std::collections::BTreeMap;
pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> anyhow::Result<()> {
pub async fn monitor_expirations(
data: &Data,
blocks: u32,
_sessions: u32,
) -> Result<(), subxt::Error> {
let client = data.client();
let indexer = data.indexer.clone();
......@@ -27,7 +30,7 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an
let end_block = current_block + blocks;
let mut identity_cache = cache::IdentityCache::new(client.clone(), indexer);
let mut identity_cache = IdentityCache::new(client.clone(), indexer);
// Certifications
let mut basic_certs_iter = client
......@@ -36,10 +39,10 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an
.iter(runtime::storage().certification().certs_removable_on_iter())
.await?;
let mut basic_certs = BTreeMap::new();
while let Some(Ok((k, v))) = basic_certs_iter.next().await {
let block_number = BlockNumber::from_le_bytes(k[40..44].try_into().unwrap());
while let Some(Ok(item)) = basic_certs_iter.next().await {
let block_number = BlockNumber::from_le_bytes(item.key_bytes[40..44].try_into().unwrap());
if block_number < end_block {
basic_certs.insert(block_number - current_block, v);
basic_certs.insert(block_number - current_block, item.value);
}
}
......@@ -51,12 +54,12 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an
println!(
" {} ({}) -> {} ({})",
identity_cache
.fetch_identity(issuer_id, parent_hash)
.fetch_identity(issuer_id,)
.await
.unwrap_or_else(|_| "?".into()),
issuer_id,
identity_cache
.fetch_identity(receiver_id, parent_hash)
.fetch_identity(receiver_id,)
.await
.unwrap_or_else(|_| "?".into()),
receiver_id,
......@@ -68,17 +71,18 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an
// Memberships
let mut basic_membership_iter = client
.storage()
.at(parent_hash)
.at_latest()
.await?
.iter(runtime::storage().membership().memberships_expire_on_iter())
.await?;
let mut basic_memberships = BTreeMap::new();
while let Some(Ok((k, v))) = basic_membership_iter.next().await {
let block_number = BlockNumber::from_le_bytes(k[40..44].try_into().unwrap());
while let Some(Ok(item)) = basic_membership_iter.next().await {
let block_number = BlockNumber::from_le_bytes(item.key_bytes[40..44].try_into().unwrap());
if block_number < end_block {
if block_number < current_block {
dbg!((block_number, current_block));
}
basic_memberships.insert(block_number - current_block, v);
basic_memberships.insert(block_number - current_block, item.value);
}
}
......@@ -90,7 +94,7 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an
println!(
" {} ({})",
identity_cache
.fetch_identity(identity_id, parent_hash)
.fetch_identity(identity_id)
.await
.unwrap_or_else(|_| "?".into()),
identity_id,
......@@ -101,3 +105,53 @@ pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> an
Ok(())
}
use std::collections::{hash_map, HashMap};
pub struct IdentityCache {
client: Client,
identities: HashMap<IdtyId, String>,
indexer: Option<Indexer>,
}
impl IdentityCache {
pub fn new(client: Client, indexer: Option<Indexer>) -> Self {
Self {
client,
identities: HashMap::new(),
indexer,
}
}
pub async fn fetch_identity(&mut self, identity_id: IdtyId) -> Result<String, GcliError> {
Ok(match self.identities.entry(identity_id) {
hash_map::Entry::Occupied(entry) => entry.get().clone(),
hash_map::Entry::Vacant(entry) => entry
.insert({
let pubkey = self
.client
.storage()
.at_latest()
.await?
.fetch(&runtime::storage().identity().identities(identity_id))
.await?
.ok_or_else(|| anyhow!("Identity {} not found", identity_id))?
.owner_key
.to_string();
format!(
"“ {} ”",
if let Some(indexer) = &self.indexer {
if let Some(username) = indexer.username_by_pubkey(&pubkey).await {
username
} else {
pubkey
}
} else {
pubkey
}
)
})
.clone(),
})
}
}
This diff is collapsed.
......@@ -4,7 +4,7 @@ use sp_core::DeriveJunction;
use subxt::ext::sp_runtime::MultiAddress;
pub async fn repart(data: &Data, target: u32, actual_repart: Option<u32>) -> anyhow::Result<()> {
let KeyPair::Sr25519(keypair) = data.keypair() else {
let KeyPair::Sr25519(keypair) = data.keypair().await else {
panic!("Cesium keys not implemented there")
};
let mut pairs = Vec::new();
......@@ -43,7 +43,7 @@ pub async fn repart(data: &Data, target: u32, actual_repart: Option<u32>) -> any
}
pub async fn spam_roll(data: &Data, actual_repart: usize) -> anyhow::Result<()> {
let KeyPair::Sr25519(keypair) = data.keypair() else {
let KeyPair::Sr25519(keypair) = data.keypair().await else {
panic!("Cesium keys not implemented there")
};
let client = data.client();
......@@ -65,13 +65,12 @@ pub async fn spam_roll(data: &Data, actual_repart: usize) -> anyhow::Result<()>
let dest: AccountId = pairs[i + 1].1.clone();
let watcher = client
.tx()
.create_signed_with_nonce(
.create_signed_offline(
&runtime::tx()
.balances()
.transfer_allow_death(MultiAddress::Id(dest).into(), 1),
&pairs[i].0,
nonce,
DefaultExtrinsicParamsBuilder::new().build(),
DefaultExtrinsicParamsBuilder::new().nonce(nonce).build(),
)?
.submit_and_watch()
.await?;
......
......@@ -65,7 +65,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
}
/// get balance of oneshot account
pub async fn oneshot_account_balance(data: &Data) -> Result<(), anyhow::Error> {
pub async fn oneshot_account_balance(data: &Data) -> Result<(), subxt::Error> {
println!(
"balance of oneshot account {} is: {}",
data.address(),
......@@ -93,7 +93,7 @@ pub async fn create_oneshot_account(
) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::oneshot_account::events::OneshotAccountCreated,
Payload<runtime::oneshot_account::calls::types::CreateOneshotAccount>,
StaticPayload<runtime::oneshot_account::calls::types::CreateOneshotAccount>,
>(
data,
&runtime::tx()
......@@ -127,7 +127,7 @@ pub async fn consume_oneshot_account(
);
submit_call_and_look_event::<
runtime::oneshot_account::events::OneshotAccountConsumed,
Payload<runtime::oneshot_account::calls::types::ConsumeOneshotAccount>,
StaticPayload<runtime::oneshot_account::calls::types::ConsumeOneshotAccount>,
>(data, payload)
.await
}
......@@ -174,7 +174,7 @@ pub async fn consume_oneshot_account_with_remaining(
submit_call_and_look_event::<
runtime::oneshot_account::events::OneshotAccountConsumed,
Payload<runtime::oneshot_account::calls::types::ConsumeOneshotAccountWithRemaining>,
StaticPayload<runtime::oneshot_account::calls::types::ConsumeOneshotAccountWithRemaining>,
>(data, payload)
.await
}
......@@ -11,6 +11,12 @@ pub async fn handle_command() -> Result<(), GcliError> {
// Step 1: Get actual version of gcli
const VERSION: &str = env!("CARGO_PKG_VERSION");
// Fetch the latest tags from the remote repository
Command::new("git")
.args(["fetch", "--tags"])
.status()
.map_err(|e| anyhow!(e))?;
// Step 2: Check if the git tag already exists
let tag_check_output = Command::new("git").args(["tag", "-l", VERSION]).output()?;
......@@ -28,6 +34,7 @@ pub async fn handle_command() -> Result<(), GcliError> {
Ok(true) => {
// User confirmed, proceed publishing
// Step 3: Create and push the git tag
Command::new("git")
.args(["tag", "-a", VERSION, "-m", &format!("Release v{VERSION}")])
.status()
......
......@@ -4,15 +4,15 @@ use crate::*;
// use crate::runtime::runtime_types::pallet_identity::types::RevocationPayload;
type EncodedRevocationPayload = Vec<u8>;
pub fn print_revoc_sig(data: &Data) {
let (_, signature) = generate_revoc_doc(data);
pub async fn print_revoc_sig(data: &Data) {
let (_, signature) = generate_revoc_doc(data).await;
println!("revocation payload signature");
println!("0x{}", hex::encode(signature));
}
pub fn generate_revoc_doc(data: &Data) -> (EncodedRevocationPayload, sr25519::Signature) {
pub async fn generate_revoc_doc(data: &Data) -> (EncodedRevocationPayload, sr25519::Signature) {
let payload = (b"revo", data.genesis_hash, data.idty_index()).encode();
let KeyPair::Sr25519(keypair) = data.keypair() else {
let KeyPair::Sr25519(keypair) = data.keypair().await else {
panic!("Cesium keys not implemented there")
};
let signature = keypair.sign(&payload);
......
......@@ -111,14 +111,6 @@ pub async fn runtime_info(data: Data) {
);
// currency
println!("--- currency ---");
println!(
"new account price: {}",
getf(consts.account().new_account_price())
);
println!(
"max new accounts per block: {}",
getu32(consts.account().max_new_accounts_per_block())
);
println!(
"existential deposit: {}",
getf(consts.balances().existential_deposit())
......@@ -151,10 +143,6 @@ pub async fn runtime_info(data: Data) {
"UD reeval period: {}",
getu64(consts.universal_dividend().ud_reeval_period())
);
println!(
"units per ud: {}",
getf(consts.universal_dividend().units_per_ud())
);
// todo treasury, technical committee, transaction payment, authority members
// consts.system().ss58_prefix()
}
use crate::*;
use commands::identity::try_get_idty_index_by_name;
#[cfg(feature = "gdev")]
use runtime::runtime_types::gdev_runtime::opaque::SessionKeys as RuntimeSessionKeys;
use std::collections::HashMap;
use std::ops::Deref;
type SessionKeys = [u8; 128];
......@@ -11,24 +13,16 @@ type SessionKeys = [u8; 128];
fn session_keys_decode(session_keys: SessionKeys) -> RuntimeSessionKeys {
RuntimeSessionKeys {
grandpa: runtime::runtime_types::sp_consensus_grandpa::app::Public(
runtime::runtime_types::sp_core::ed25519::Public(
session_keys[0..32].try_into().unwrap(),
),
session_keys[0..32].try_into().unwrap(),
),
babe: runtime::runtime_types::sp_consensus_babe::app::Public(
runtime::runtime_types::sp_core::sr25519::Public(
session_keys[32..64].try_into().unwrap(),
),
session_keys[32..64].try_into().unwrap(),
),
im_online: runtime::runtime_types::pallet_im_online::sr25519::app_sr25519::Public(
runtime::runtime_types::sp_core::sr25519::Public(
session_keys[64..96].try_into().unwrap(),
),
session_keys[64..96].try_into().unwrap(),
),
authority_discovery: runtime::runtime_types::sp_authority_discovery::app::Public(
runtime::runtime_types::sp_core::sr25519::Public(
session_keys[96..128].try_into().unwrap(),
),
session_keys[96..128].try_into().unwrap(),
),
}
}
......@@ -57,16 +51,23 @@ pub enum Subcommand {
/// List online authorities
ShowOnline,
/// Invite identity to become smith
Invite { target: IdtyId },
Invite {
#[clap(value_name = "USERNAME")]
target: String,
},
/// Accept invitation
Accept,
/// Certify smith
Certify { target: IdtyId },
#[clap(alias = "cert")]
Certify {
#[clap(value_name = "USERNAME")]
target: String,
},
}
/// handle smith commands
pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
let mut data = data.build_client().await?;
let mut data = data.build_client().await?.build_indexer().await?;
match command {
Subcommand::GoOnline => {
go_online(&data).await?;
......@@ -91,9 +92,15 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
commands::expire::monitor_expirations(&data, blocks, sessions).await?
}
Subcommand::ShowOnline => online(&data).await?,
Subcommand::Invite { target } => invite_smith(&data, target).await?,
Subcommand::Invite { target } => {
let target = try_get_idty_index_by_name(&data, &target).await?;
invite_smith(&data, target).await?
}
Subcommand::Accept => accept_invitation(&data).await?,
Subcommand::Certify { target } => certify_smith(&data, target).await?,
Subcommand::Certify { target } => {
let target = try_get_idty_index_by_name(&data, &target).await?;
certify_smith(&data, target).await?
}
};
Ok(())
......@@ -101,20 +108,20 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
/// rotate session keys
/// (needs to be connected to unsafe RPC)
pub async fn rotate_keys(data: &Data) -> Result<SessionKeys, anyhow::Error> {
pub async fn rotate_keys(data: &Data) -> Result<SessionKeys, GcliError> {
data.legacy_rpc_methods()
.await
.author_rotate_keys()
.await
.map_err(|e| {
anyhow!(
GcliError::Duniter(format!(
"Please make sure you are connected to your validator node with the unsafe RPC \
API enabled {e}"
)
))
})?
.deref()
.try_into()
.map_err(|e| anyhow!("Session keys have wrong length: {:?}", e))
.map_err(|e| GcliError::Duniter(format!("Session keys have wrong length: {:?}", e)))
}
/// set session keys
......@@ -122,7 +129,7 @@ pub async fn set_session_keys(
data: &Data,
session_keys: RuntimeSessionKeys,
) -> Result<TxProgress, subxt::Error> {
submit_call::<Payload<runtime::authority_members::calls::types::SetSessionKeys>>(
submit_call::<StaticPayload<runtime::authority_members::calls::types::SetSessionKeys>>(
data,
&runtime::tx()
.authority_members()
......@@ -165,7 +172,7 @@ pub async fn go_online(data: &Data) -> Result<(), GcliError> {
submit_call_and_look_event::<
runtime::authority_members::events::MemberGoOnline,
Payload<runtime::authority_members::calls::types::GoOnline>,
StaticPayload<runtime::authority_members::calls::types::GoOnline>,
>(data, &runtime::tx().authority_members().go_online())
.await
.map_err(|e| e.into())
......@@ -175,48 +182,26 @@ pub async fn go_online(data: &Data) -> Result<(), GcliError> {
pub async fn go_offline(data: &Data) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::authority_members::events::MemberGoOffline,
Payload<runtime::authority_members::calls::types::GoOffline>,
StaticPayload<runtime::authority_members::calls::types::GoOffline>,
>(data, &runtime::tx().authority_members().go_offline())
.await
}
/// get online authorities
pub async fn online(data: &Data) -> Result<(), anyhow::Error> {
pub async fn online(data: &Data) -> Result<(), subxt::Error> {
let client = data.client();
let indexer = data.indexer.clone();
let parent_hash = client
.clone()
let online_authorities = client
.storage()
.at_latest()
.await?
.fetch(&runtime::storage().system().parent_hash())
.await?
.unwrap();
let mut identity_cache = cache::IdentityCache::new(client.clone(), indexer);
let online_authorities = client
.storage()
.at(parent_hash)
.fetch(&runtime::storage().authority_members().online_authorities())
.await?
.unwrap_or_default();
println!("Online:");
for identity_id in online_authorities {
println!(
" {}",
identity_cache
.fetch_identity(identity_id, parent_hash)
.await
.unwrap_or_else(|_| format!("{identity_id}"))
);
}
let incoming_authorities = client
.storage()
.at(parent_hash)
.at_latest()
.await?
.fetch(
&runtime::storage()
.authority_members()
......@@ -224,21 +209,10 @@ pub async fn online(data: &Data) -> Result<(), anyhow::Error> {
)
.await?
.unwrap_or_default();
println!("Incoming:");
for identity_id in incoming_authorities {
println!(
" {}",
identity_cache
.fetch_identity(identity_id, parent_hash)
.await
.unwrap_or_else(|_| format!("{identity_id}"))
);
}
let outgoing_authorities = client
.storage()
.at(parent_hash)
.at_latest()
.await?
.fetch(
&runtime::storage()
.authority_members()
......@@ -247,15 +221,62 @@ pub async fn online(data: &Data) -> Result<(), anyhow::Error> {
.await?
.unwrap_or_default();
println!("Outgoing:");
for identity_id in outgoing_authorities {
if let Some(indexer) = &data.indexer {
let mut names = HashMap::<IdtyId, String>::new();
indexer
.names_by_indexes(&online_authorities)
.await
.into_iter()
.for_each(|e| {
names.insert(e.0, e.1);
});
println!("Online:");
println!(
" {}",
identity_cache
.fetch_identity(identity_id, parent_hash)
.await
.unwrap_or_else(|_| format!("{identity_id}"))
"{}",
online_authorities
.iter()
.map(|i| names
.get(i)
.map(|n| n.to_string())
.unwrap_or(format!("{} <no name found>", i)))
.collect::<Vec<String>>()
.join(", ")
);
println!("Incoming:");
println!(
"{}",
incoming_authorities
.iter()
.map(|i| names
.get(i)
.map(|n| n.to_string())
.unwrap_or(format!("{} <no name found>", i)))
.collect::<Vec<String>>()
.join(", ")
);
println!("Outgoing:");
println!(
"{}",
outgoing_authorities
.iter()
.map(|i| names
.get(i)
.map(|n| n.to_string())
.unwrap_or(format!("{} <no name found>", i)))
.collect::<Vec<String>>()
.join(", ")
);
} else {
println!("Online:");
println!("{online_authorities:?}");
println!("Incoming:");
println!("{incoming_authorities:?}");
println!("Outgoing:");
println!("{outgoing_authorities:?}");
}
Ok(())
......@@ -265,7 +286,7 @@ pub async fn online(data: &Data) -> Result<(), anyhow::Error> {
pub async fn invite_smith(data: &Data, target: IdtyId) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::smith_members::events::InvitationSent,
Payload<runtime::smith_members::calls::types::InviteSmith>,
StaticPayload<runtime::smith_members::calls::types::InviteSmith>,
>(data, &runtime::tx().smith_members().invite_smith(target))
.await
}
......@@ -274,7 +295,7 @@ pub async fn invite_smith(data: &Data, target: IdtyId) -> Result<(), subxt::Erro
pub async fn accept_invitation(data: &Data) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::smith_members::events::InvitationAccepted,
Payload<runtime::smith_members::calls::types::AcceptInvitation>,
StaticPayload<runtime::smith_members::calls::types::AcceptInvitation>,
>(data, &runtime::tx().smith_members().accept_invitation())
.await
}
......@@ -283,7 +304,7 @@ pub async fn accept_invitation(data: &Data) -> Result<(), subxt::Error> {
pub async fn certify_smith(data: &Data, target: IdtyId) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::smith_members::events::SmithCertAdded,
Payload<runtime::smith_members::calls::types::CertifySmith>,
StaticPayload<runtime::smith_members::calls::types::CertifySmith>,
>(data, &runtime::tx().smith_members().certify_smith(target))
.await
}
......@@ -33,7 +33,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
pub async fn set_key(data: &Data, new_key: AccountId) -> Result<(), subxt::Error> {
submit_call_and_look_event::<
runtime::sudo::events::KeyChanged,
Payload<runtime::sudo::calls::types::SetKey>,
StaticPayload<runtime::sudo::calls::types::SetKey>,
>(data, &runtime::tx().sudo().set_key(new_key.into()))
.await
}
......@@ -44,7 +44,7 @@ pub async fn set_distance_ok(data: &Data, identity: IdtyId) -> Result<(), subxt:
let inner = runtime::Call::Distance(inner);
submit_call_and_look_event::<
runtime::sudo::events::Sudid,
Payload<runtime::sudo::calls::types::Sudo>,
StaticPayload<runtime::sudo::calls::types::Sudo>,
>(data, &runtime::tx().sudo().sudo(inner))
.await
}