Commit b7c6bc89 authored by Éloïs's avatar Éloïs
Browse files

chore: move gva requests into lib dubp_client

parent 8e119375
Pipeline #11198 failed with stages
in 13 minutes and 25 seconds
This diff is collapsed.
......@@ -9,16 +9,14 @@ description = "A command line client written in Rust that use Duniter GVA API."
[dependencies]
anyhow = "1.0.32"
dup-crypto = { version = "0.43.2", features = [ "bip32-ed25519", "dewif", "mnemonic"] }
graphql_client = "0.9.0"
reqwest = { version = "0.11.1", features = ["blocking", "json"] }
dubp-client = { git = "https://git.duniter.org/libs/dubp-rs-client-lib", branch = "master", features = ["blocking"] }
#dubp-client= { path = "../dubp-rs-client-lib", features = ["blocking"] }
rpassword = "5.0.1"
serde = { version = "1.0.105", features = ["derive"] }
structopt = "0.3.18"
[dev-dependencies]
dubp-client = { git = "https://git.duniter.org/libs/dubp-rs-client-lib", branch = "master", features = ["blocking", "mock"] }
#dubp-client= { path = "../dubp-rs-client-lib", features = ["blocking", "mock"] }
mockall = "0.8.0"
[build-dependencies]
duniter-gva-gql = { git = "https://git.duniter.org/nodes/typescript/duniter", branch = "dev" }
#duniter-gva-gql = { path = "../duniter/rust-libs/modules/gva/gql" }
fn main() {
let schema_sdl = duniter_gva_gql::get_schema_definition();
std::fs::write("gql/gva_schema.gql", schema_sdl.as_bytes())
.expect("Fail to write gva schema in file");
println!("cargo:rerun-if-changed=build.rs");
}
query Balance($script: String!, $withUd: Boolean!) {
balance(script: $script) {
amount
}
currentUd @include(if: $withUd) {
amount
}
}
query CurrentUd {
currentUd {
amount
}
}
query MembersCount {
currentBlock {
membersCount
}
}
......@@ -15,6 +15,7 @@
pub mod balance;
pub mod current_ud;
pub mod idty;
pub mod members_count;
pub mod wallet;
......@@ -30,6 +31,8 @@ pub(crate) enum Command {
},
/// Get current UD value
CurrentUd,
/// Get identity
Idty { pubkey: String },
/// Get current number of WoT members
MembersCount,
/// Create or update a wallet
......
......@@ -16,35 +16,39 @@
use crate::*;
pub(crate) fn balance<W: Write>(
client: &Client,
gva_endpoint: &str,
out: &mut W,
pubkey_or_script: &str,
requestor: &GvaRequestor,
ud_unit: bool,
) -> anyhow::Result<()> {
let request_body = Balance::build_query(balance::Variables {
script: pubkey_or_script.to_owned(),
with_ud: ud_unit,
});
let balance::ResponseData {
balance: balance::BalanceBalance { amount },
current_ud: current_ud_opt,
} = client.send_gql_query(&request_body)?;
if let Some(balance::BalanceCurrentUd { amount: ud_amount }) = current_ud_opt {
writeln!(
out,
"The balance of account '{}' is {:.2} UDĞ1 !",
pubkey_or_script,
amount as f64 / ud_amount as f64,
)?;
let pubkey_or_script = PubkeyOrScript::from_str(pubkey_or_script)?;
if let Some(AccountBalance {
amount,
ud_amount_opt,
}) = requestor.account_balance(gva_endpoint, &pubkey_or_script, ud_unit)?
{
if let Some(ud_amount) = ud_amount_opt {
writeln!(
out,
"The balance of account '{}' is {:.2} UDĞ1 !",
pubkey_or_script.to_string(),
amount.amount() as f64 / ud_amount.amount() as f64,
)?;
} else {
writeln!(
out,
"The balance of account '{}' is {}.{:02} Ğ1 !",
pubkey_or_script.to_string(),
amount.amount() / 100,
amount.amount() % 100
)?;
}
} else {
writeln!(
out,
"The balance of account '{}' is {}.{:02} Ğ1 !",
pubkey_or_script,
amount / 100,
amount % 100
"Account '{}' not exist !",
pubkey_or_script.to_string(),
)?;
}
Ok(())
......@@ -53,43 +57,43 @@ pub(crate) fn balance<W: Write>(
#[cfg(test)]
mod tests {
use super::*;
use dubp_client::wallet::prelude::SourceAmount;
#[test]
fn test_balance() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<balance::Variables>, _>()
.returning(|_| {
Ok(balance::ResponseData {
balance: balance::BalanceBalance { amount: 2_046 },
current_ud: None,
})
});
let mut client = GvaRequestor::default();
client.expect_account_balance().returning(|_, _, _| {
Ok(Some(AccountBalance {
amount: SourceAmount::new(2046, 0),
ud_amount_opt: None,
}))
});
let mut out = Vec::new();
balance(&client, &mut out, "toto", false)?;
balance("", &mut out, "toto", &client, false)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "The balance of account 'toto' is 20.46 Ğ1 !\n");
assert_eq!(output, "The balance of account 'SIG(toto)' is 20.46 Ğ1 !\n");
Ok(())
}
#[test]
fn test_balance_with_ud_unit() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<balance::Variables>, _>()
.returning(|_| {
Ok(balance::ResponseData {
balance: balance::BalanceBalance { amount: 2_046 },
current_ud: Some(balance::BalanceCurrentUd { amount: 1_023 }),
})
});
let mut client = GvaRequestor::default();
client.expect_account_balance().returning(|_, _, _| {
Ok(Some(AccountBalance {
amount: SourceAmount::new(2_046, 0),
ud_amount_opt: Some(SourceAmount::new(1_023, 0)),
}))
});
let mut out = Vec::new();
balance(&client, &mut out, "toto", true)?;
balance("", &mut out, "toto", &client, true)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "The balance of account 'toto' is 2.00 UDĞ1 !\n");
assert_eq!(
output,
"The balance of account 'SIG(toto)' is 2.00 UDĞ1 !\n"
);
Ok(())
}
......
......@@ -15,15 +15,14 @@
use crate::*;
pub(crate) fn current_ud<W: Write>(client: &Client, out: &mut W) -> anyhow::Result<()> {
let request_body = CurrentUd::build_query(current_ud::Variables);
if let current_ud::ResponseData {
current_ud: Some(current_ud::CurrentUdCurrentUd { amount }),
} = client.send_gql_query(&request_body)?
{
let int_part = amount / 100;
let dec_part = amount % 100;
pub(crate) fn current_ud<W: Write>(
gva_endpoint: &str,
out: &mut W,
requestor: &GvaRequestor,
) -> anyhow::Result<()> {
if let Some(current_ud) = requestor.current_ud(gva_endpoint)? {
let int_part = current_ud / 100;
let dec_part = current_ud % 100;
writeln!(
out,
"The current UD value is {}.{:02} Ğ1 !",
......@@ -42,16 +41,10 @@ mod tests {
#[test]
fn test_current_ud() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<current_ud::Variables>, _>()
.returning(|_| {
Ok(current_ud::ResponseData {
current_ud: Some(current_ud::CurrentUdCurrentUd { amount: 1_023 }),
})
});
let mut client = GvaRequestor::default();
client.expect_current_ud().returning(|_| Ok(Some(1_023)));
let mut out = Vec::new();
current_ud(&client, &mut out)?;
current_ud("", &mut out, &client)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "The current UD value is 10.23 Ğ1 !\n");
......
......@@ -15,49 +15,25 @@
use crate::*;
#[cfg_attr(test, allow(dead_code))]
pub(crate) struct Client {
inner: reqwest::blocking::Client,
server_url: String,
}
#[cfg_attr(test, mockall::automock, allow(dead_code))]
impl Client {
pub(crate) fn new(server_url: String) -> Self {
Client {
inner: reqwest::blocking::Client::new(),
server_url,
}
pub(crate) fn idty<W: Write>(
gva_endpoint: &str,
out: &mut W,
pubkey: &str,
requestor: &GvaRequestor,
) -> anyhow::Result<()> {
let pubkey = PublicKey::from_base58(pubkey)?;
if let Some(Idty {
is_member,
username,
..
}) = requestor.idty_by_pubkey(gva_endpoint, pubkey)?
{
writeln!(out, "Found identity for pubkey:")?;
writeln!(out, "username: {}", username)?;
writeln!(out, "is_member: {}", is_member)?;
} else {
writeln!(out, "No identity for pubkey {}", pubkey)?;
}
pub(crate) fn send_gql_query<
Req: 'static + serde::Serialize,
ResData: 'static + serde::de::DeserializeOwned,
>(
&self,
request_body: &Req,
) -> anyhow::Result<ResData> {
let request = self.inner.post(&self.server_url).json(request_body);
let start_time = Instant::now();
let response = request.send()?;
let req_duration = Instant::now().duration_since(start_time);
println!("The server responded in {} ms.", req_duration.as_millis());
let mut gql_response: Response<ResData> = response.json()?;
if let Some(errors) = gql_response.errors.take() {
print_server_errors(errors);
Err(anyhow::Error::msg(""))
} else if let Some(data) = gql_response.data {
Ok(data)
} else {
Err(anyhow::Error::msg("server response contains no data"))
}
}
}
fn print_server_errors(errors: Vec<graphql_client::Error>) {
println!("Server errors:");
for error in errors {
println!("{}", error);
}
Ok(())
}
......@@ -15,12 +15,12 @@
use crate::*;
pub(crate) fn members_count<W: Write>(client: &Client, out: &mut W) -> anyhow::Result<()> {
let request_body = MembersCount::build_query(members_count::Variables);
let members_count::ResponseData {
current_block: members_count::MembersCountCurrentBlock { members_count },
} = client.send_gql_query(&request_body)?;
pub(crate) fn members_count<W: Write>(
gva_endpoint: &str,
out: &mut W,
requestor: &GvaRequestor,
) -> anyhow::Result<()> {
let members_count = requestor.members_count(gva_endpoint)?;
writeln!(
out,
......@@ -39,18 +39,10 @@ mod tests {
#[test]
fn test_member_count() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<members_count::Variables>, _>()
.returning(|_| {
Ok(members_count::ResponseData {
current_block: members_count::MembersCountCurrentBlock {
members_count: 10_000,
},
})
});
let mut client = GvaRequestor::default();
client.expect_members_count().returning(|_| Ok(10_000));
let mut out = Vec::new();
members_count(&client, &mut out)?;
members_count("", &mut out, &client)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "There is currently 10000 members in Ğ1 WoT!\n");
......
......@@ -14,14 +14,17 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*;
use dup_crypto::keys::{ed25519::bip32::KeyPair, KeyPair as _};
use dup_crypto::mnemonic::{mnemonic_to_seed, Language, Mnemonic, MnemonicType};
use dup_crypto::{
use dubp_client::crypto::{
bases::b58::ToBase58,
dewif::{
read_dewif_file_content, write_dewif_v4_content, Currency, ExpectedCurrency, G1_CURRENCY,
},
keys::ed25519::bip32::DerivationIndex,
keys::{
ed25519::bip32::{KeyPair, PrivateDerivationPath},
KeyPair as _, KeyPairEnum,
},
mnemonic::{mnemonic_to_seed, Language, Mnemonic, MnemonicType},
utils::U31,
};
#[derive(StructOpt)]
......@@ -112,25 +115,19 @@ pub(crate) fn wallet<W: Write>(out: &mut W, command: WalletCommand) -> anyhow::R
let dewif_content = get_dewif_content(dewif, dewif_file)?;
let keypair = get_keypair_from_dewif(dewif_content, password_stdin)?;
let account_keypair = keypair.derive(DerivationIndex::hard(account_index)?);
if external {
let external_keypair = account_keypair.derive(DerivationIndex::hard(0)?);
let derivation_path = PrivateDerivationPath::opaque(
U31::new(account_index)?,
external,
if let Some(address_index) = address_index {
let address_keypair =
external_keypair.derive(DerivationIndex::soft(address_index)?);
writeln!(out, "{}", address_keypair.public_key().to_base58())?;
Some(U31::new(address_index)?)
} else {
writeln!(out, "{}", external_keypair.public_key().to_base58())?;
}
} else if let Some(address_index) = address_index {
let internal_keypair = account_keypair.derive(DerivationIndex::hard(1)?);
let address_keypair =
internal_keypair.derive(DerivationIndex::soft(address_index)?);
writeln!(out, "{}", address_keypair.public_key().to_base58())?;
} else {
writeln!(out, "{}", account_keypair.public_key().to_base58())?;
}
None
},
)?;
let derived_keypair = keypair.derive(derivation_path);
writeln!(out, "{}", derived_keypair.public_key().to_base58())?;
Ok(())
}
......@@ -207,7 +204,7 @@ fn get_keypair_from_dewif(dewif_content: String, password_stdin: bool) -> anyhow
&password,
)?;
if let Some(dup_crypto::keys::KeyPairEnum::Bip32Ed25519(keypair)) = keypairs.next() {
if let Some(KeyPairEnum::Bip32Ed25519(keypair)) = keypairs.next() {
Ok(keypair)
} else {
Err(anyhow::Error::msg("DEWIF corrupted"))
......
......@@ -24,42 +24,33 @@
unused_import_braces
)]
mod client;
mod commands;
#[cfg(not(test))]
use crate::client::Client;
#[cfg(test)]
use crate::client::MockClient as Client;
use crate::commands::Command;
use commands::{
balance::balance, current_ud::current_ud, members_count::members_count, wallet::wallet,
balance::balance, current_ud::current_ud, idty::idty, members_count::members_count,
wallet::wallet,
};
use dubp_client::{
crypto::keys::{ed25519::PublicKey, PublicKey as _},
AccountBalance, Idty, PubkeyOrScript,
};
use graphql_client::GraphQLQuery;
use graphql_client::Response;
#[cfg(not(test))]
use dubp_client::GvaRequestor;
#[cfg(test)]
use dubp_client::MockGvaRequestor as GvaRequestor;
use std::{
env::var_os,
fs::File,
io::{BufReader, Read, Write},
path::PathBuf,
time::Instant,
str::FromStr,
};
use structopt::StructOpt;
const DEFAULT_GVA_SERVER: &str = "https://g1.librelois.fr/gva";
#[derive(Debug, Clone, Copy, GraphQLQuery)]
#[graphql(schema_path = "gql/gva_schema.gql", query_path = "gql/gva_queries.gql")]
pub struct Balance;
#[derive(Debug, Clone, Copy, GraphQLQuery)]
#[graphql(schema_path = "gql/gva_schema.gql", query_path = "gql/gva_queries.gql")]
pub struct CurrentUd;
#[derive(Debug, Clone, Copy, GraphQLQuery)]
#[graphql(schema_path = "gql/gva_schema.gql", query_path = "gql/gva_queries.gql")]
pub struct MembersCount;
#[derive(StructOpt)]
#[structopt(name = "rust-gva-client", about = "Client use GVA API of Duniter.")]
struct CliArgs {
......@@ -73,16 +64,23 @@ struct CliArgs {
fn main() -> anyhow::Result<()> {
let cli_args = CliArgs::from_args();
let client = Client::new(cli_args.server);
let gva_requestor = GvaRequestor::new();
let mut out = std::io::stdout();
match cli_args.command {
Command::Balance {
pubkey_or_script,
ud_unit,
} => balance(&client, &mut out, &pubkey_or_script, ud_unit)?,
Command::CurrentUd => current_ud(&client, &mut out)?,
Command::MembersCount => members_count(&client, &mut out)?,
} => balance(
&cli_args.server,
&mut out,
&pubkey_or_script,
&gva_requestor,
ud_unit,
)?,
Command::CurrentUd => current_ud(&cli_args.server, &mut out, &gva_requestor)?,
Command::Idty { pubkey } => idty(&cli_args.server, &mut out, &pubkey, &gva_requestor)?,
Command::MembersCount => members_count(&cli_args.server, &mut out, &gva_requestor)?,
Command::Wallet { command } => wallet(&mut out, command)?,
}
Ok(())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment