Newer
Older
#[subxt::subxt(
runtime_metadata_path = "res/metadata.scale",
derive_for_all_types = "Debug"
)]
pub mod runtime {
// IF NEEDED
// #[subxt(substitute_type = "spcore::sr25519::Signature")]
// use crate::gdev::runtime_types::sp_core::sr25519::Signature;
}
pub type Client = subxt::OnlineClient<Runtime>;
pub type AccountId = subxt::ext::sp_runtime::AccountId32;
pub type TxInBlock = subxt::tx::TxInBlock<Runtime, Client>;
pub type TxProgress = subxt::tx::TxProgress<Runtime, Client>;
pub type Balance = u64;
pub type AccountData = runtime::runtime_types::pallet_duniter_account::types::AccountData<Balance>;
pub type AccountInfo = runtime::runtime_types::frame_system::AccountInfo<u32, AccountData>;
type Hashing = subxt::ext::sp_runtime::traits::BlakeTwo256;
type Address = subxt::ext::sp_runtime::MultiAddress<Self::AccountId, u32>;
type Header = subxt::ext::sp_runtime::generic::Header<
Self::BlockNumber,
subxt::ext::sp_runtime::traits::BlakeTwo256,
>;
type Signature = subxt::ext::sp_runtime::MultiSignature;
type ExtrinsicParams = subxt::tx::BaseExtrinsicParams<Self, Tip>;
// Tip for transaction fee
#[derive(Copy, Clone, Debug, Default, codec::Encode)]
pub fn new(amount: u64) -> Self {
Tip { tip: amount }
}
// define command line arguments
#[derive(Clone, clap::Parser, Debug, Default)]
#[clap(subcommand)]
pub subcommand: Subcommand,
#[clap(short, long)]
indexer: Option<String>,
/// Do not use indexer
#[clap(long)]
no_indexer: bool,
/// Secret key or BIP39 mnemonic
/// (eventually followed by derivation path)
#[clap(short, long)]
secret: Option<String>,
/// Secret key format (seed, substrate)
#[clap(short = 'S', long, default_value = SecretFormat::Substrate)]
secret_format: SecretFormat,
/// Address
#[clap(short, long)]
address: Option<String>,
/// Overwrite duniter websocket RPC endpoint
#[clap(short, long)]
url: Option<String>,
/// Chose target network
#[clap(short, long)]
network: Option<String>,
use subxt::blocks::ExtrinsicEvents;
use subxt::tx::TxStatus;
/// track progress of transaction on the network
/// until it is in block with success or failure
pub async fn track_progress(
mut progress: TxProgress,
) -> Result<ExtrinsicEvents<Runtime>, subxt::Error> {
loop {
if let Some(status) = progress.next_item().await {
match status? {
TxStatus::Ready => {
println!("transaction submitted to the network, waiting 6 seconds...");
}
TxStatus::InBlock(in_block) => break in_block,
TxStatus::Invalid => {
println!("Invalid");
}
_ => continue,
}
}
}
.wait_for_success()
.await
}
/// custom error type intended to provide more convenient error message to user
#[derive(Debug)]
pub enum GcliError {
/// error coming from subxt
Subxt(subxt::Error),
/// error coming from duniter
Duniter(String),
/// error coming from indexer
Indexer(String),
/// logic error (illegal operation or security)
Logic(String),
/// error coming from anyhow
Anyhow(anyhow::Error),
}
impl std::fmt::Display for GcliError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for GcliError {}
impl From<subxt::Error> for GcliError {
fn from(e: subxt::Error) -> GcliError {
GcliError::Subxt(e)
}
}
impl From<anyhow::Error> for GcliError {
fn from(e: anyhow::Error) -> GcliError {
GcliError::Anyhow(e)
}
}
#[derive(Clone, Debug, clap::Subcommand, Default)]
/// Fetch account balance TODO also oneshot account
/// Show address corresponding to given arguments
ShowAddress,
/// Fetch identity
Identity {
#[clap(short = 'p', long = "pubkey")]
account_id: Option<AccountId>,
#[clap(short = 'i', long = "identity")]
identity_id: Option<u32>,
#[clap(short = 'u', long = "username")]
username: Option<String>,
},
/// Create and certify an identity
///
/// Caller must be member, and the target account must exist.
CreateIdentity {
/// Confirm an identity
///
/// To be called by the certified not-yet-member account, to become member.
ConfirmIdentity {
name: String,
},
/// Generate a revocation document for the provided account
GenRevocDoc,
/// Revoke an identity immediately
RevokeIdentity,
#[clap(long = "oneshot")]
dest_oneshot: bool,
},
ConsumeOneshotWithRemaining {
balance: u64,
#[clap(long = "rem-one")]
remaining_to_oneshot: bool,
},
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/// List upcoming expirations that require an action
Expire {
/// Show certs that expire within less than this number of blocks
#[clap(short, long, default_value_t = 100800)]
blocks: u32,
/// Show authorities that should rotate keys within less than this number of sessions
#[clap(short, long, default_value_t = 100)]
sessions: u32,
},
GoOffline,
GoOnline,
/// List online authorities
Online,
#[clap(hide = true)]
Repart {
// Number of transactions per block to target
target: u32,
#[clap(short = 'o', long = "old-repart")]
// Old/actual repartition
actual_repart: Option<u32>,
},
#[clap(hide = true)]
SpamRoll {
actual_repart: usize,
},
SudoSetKey {
/// Emit a smith certification
SmithCert {
to: u32,
},
/// List members of the technical committee
TechMembers,
/// List proposals to the technical committee
TechProposals,
/// Vote a proposal to the technical committee
TechVote {
/// Proposal hash
hash: H256,
/// Proposal index
index: u32,
/// Vote (0=against, 1=for)
vote: u8,
},
Transfer {
/// Amount to transfer
amount: u64,
/// Destination address
/// Prevent from going below account existential deposit
#[clap(short = 'k', long = "keep-alive")]
keep_alive: bool,
},
/// Transfer the same amount for each space-separated address.
/// If an address appears mutiple times, it will get multiple times the same amount
TransferMultiple {
/// Amount given to each destination address
amount: u64,
/// List of target addresses
},
/// Rotate and set session keys
UpdateKeys,
/// Check current block
CurrentBlock,
Indexer(indexer::Subcommand),
/// Config subcommands
#[clap(subcommand)]
Config(conf::Subcommand),
Subcommand::GetBalance => {
data = data
.fetch_system_properties()
.await?;
commands::account::get_balance(data).await?
}
Subcommand::ShowAddress => {
data = data.build_address();
println!("address is: {}", data.address());
}
Subcommand::CreateIdentity { target } => {
data = data.build_client().await?.build_keypair();
commands::identity::create_identity(data.keypair(), data.client(), target).await?;
data = data.build_client().await?.build_keypair();
commands::identity::confirm_identity(data.keypair(), data.client(), name).await?;
Subcommand::RevokeIdentity => {
data = data
.build_client()
commands::identity::revoke_identity(data).await?;
Subcommand::CreateOneshot { balance, dest } => {
data = data.build_client().await?;
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
}
Subcommand::ConsumeOneshot { dest, dest_oneshot } => {
data = data.build_client().await?;
commands::oneshot::consume_oneshot_account(
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
}
Subcommand::ConsumeOneshotWithRemaining {
balance,
dest,
dest_oneshot,
remaining_to,
remaining_to_oneshot,
} => {
data = data.build_client().await?;
commands::oneshot::consume_oneshot_account_with_remaining(
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
balance,
dest,
dest_oneshot,
remaining_to,
remaining_to_oneshot,
)
}
Subcommand::Expire { blocks, sessions } => {
data = data.build_client().await?;
commands::expire::monitor_expirations(&data, blocks, sessions).await?
}
Subcommand::Identity {
ref account_id,
identity_id,
ref username,
} => {
data = data.build_client().await?;
account_id.clone(),
identity_id,
username.clone(),
)
.await?
}
Subcommand::GenRevocDoc => {
.build_keypair()
.fetch_idty_index()
.await?;
commands::revocation::print_revoc_sig(&data)
data = data.build_client().await?;
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
data = data.build_client().await?;
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
}
Subcommand::OneshotBalance { account } => {
data = data.build_client().await?;
commands::oneshot::oneshot_account_balance(data.client(), account).await?
data = data.build_client().await?;
}
Subcommand::Repart {
target,
actual_repart,
} => {
data = data.build_client().await?;
commands::net_test::repart(
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
target,
actual_repart,
)
.await?
}
Subcommand::SpamRoll { actual_repart } => {
data = data.build_client().await?;
commands::net_test::spam_roll(
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
actual_repart,
)
.await?
}
Subcommand::SudoSetKey { new_key } => {
data = data.build_keypair().build_client().await?;
commands::sudo::set_key(data.keypair(), data.client(), new_key).await?;
Subcommand::SmithCert { to } => {
data = data
.build_client()
.build_keypair()
.fetch_idty_index()
.await?;
commands::smith::cert(data.client(), data.keypair(), data.idty_index(), to).await?
}
data = data.build_client().await?;
commands::collective::technical_committee_members(&data).await?
data = data.build_client().await?;
commands::collective::technical_committee_proposals(data.client()).await?
}
Subcommand::TechVote { hash, index, vote } => {
data = data.build_client().await?;
let vote = match vote {
0 => false,
1 => true,
_ => panic!("Vote must be written 0 if you disagree, or 1 if you agree."),
};
commands::collective::technical_committee_vote(
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
hash, //H256::from_str(&hash).expect("Invalid hash formatting"),
index,
vote,
)
}
Subcommand::Transfer {
amount,
dest,
keep_alive,
} => {
data = data.build_client().await?;
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
}
Subcommand::TransferMultiple { amount, dests } => {
data = data.build_client().await?;
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
data = data.build_client().await?;
get_keys(
args.secret_format,
&args.address,
&args.secret,
NeededKeys::Secret,
)?
.1
.unwrap(),
data.client(),
)
data = data.build_client().await?.fetch_system_properties().await?;
commands::runtime::runtime_info(data).await;
}
Subcommand::CurrentBlock => {
data = data.build_client().await?;
"current block on {}: {}",
data.cfg.duniter_endpoint,
data.client()
.storage()
.fetch(&runtime::storage().system().number(), None)
.await?
.unwrap()
);
}
Subcommand::Indexer(subcommand) => indexer::handle_command(data, subcommand).await?,
Subcommand::Config(subcommand) => conf::handle_command(data, subcommand)?,