From a404efadf764ee02fadf148881f8427fb9b7602b Mon Sep 17 00:00:00 2001 From: cgeek <cem.moreau@gmail.com> Date: Wed, 3 Nov 2021 12:50:43 +0100 Subject: [PATCH] feat: add `adhere` command --- src/bma/wot/mod.rs | 20 +++++++++++++ src/cli/adhere.rs | 49 ++++++++++++++++++++++++++++++++ src/cli/mod.rs | 39 ++++++++++++++++--------- src/dubp/documents/membership.rs | 23 +++++++++++++++ src/dubp/documents/mod.rs | 3 +- 5 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 src/cli/adhere.rs create mode 100644 src/dubp/documents/membership.rs diff --git a/src/bma/wot/mod.rs b/src/bma/wot/mod.rs index 9046a95..6eaac05 100644 --- a/src/bma/wot/mod.rs +++ b/src/bma/wot/mod.rs @@ -9,6 +9,11 @@ struct BmaCertification { cert: String } +#[derive(Serialize, Deserialize)] +struct BmaMembership { + membership: String +} + impl Display for BmaCertification { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(self.cert.as_str()) @@ -24,6 +29,21 @@ pub fn certify(node: &BmaNode, idty: &String, sig: &String) -> Result<(), String .send() .expect("Error during POST /wot/certify"); + if resp.status() != 200 { + return Err(format!("KO status : {}, message: {}", resp.status(), resp.text().unwrap())); + } + Ok(()) +} + +pub fn membership(node: &BmaNode, idty: &String, sig: &String) -> Result<(), String> { + let ms = BmaMembership { membership: format!("{}{}\n", idty, sig) }; + let address = node.get_address(); + let client = reqwest::blocking::Client::new(); + let resp = client.post(format!("{}/blockchain/membership", address)) + .json(&ms) + .send() + .expect("Error during POST /blockchain/membership"); + if resp.status() != 200 { return Err(format!("KO status : {}, message: {}", resp.status(), resp.text().unwrap())); } diff --git a/src/cli/adhere.rs b/src/cli/adhere.rs new file mode 100644 index 0000000..41bb49d --- /dev/null +++ b/src/cli/adhere.rs @@ -0,0 +1,49 @@ +use crate::bma::BmaNode; +use crate::bma::lookup_identity::{LookupResult, lookup2identities, lookup_print}; +use crate::crypto::duniter_key::scrypt_duniter_key::ScryptDuniterKey; +use crate::crypto::duniter_key::ToDuniterKey; +use crate::dubp::documents::membership::Membership; +use crate::dubp::signable::Signable; + +pub fn adhere(node: &BmaNode) -> Result<(), String> { + + println!("Deriving key for signature..."); + let keyring = ScryptDuniterKey::new(String::from("fakesalt"), String::from("fakepasswd")).derive(); // TODO: get salt/passwd from file/cli + + let uid_or_pub = keyring.get_public_base58(); + let address = node.get_address(); + println!("Fetching identity using \"{}\" pattern...", uid_or_pub); + let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node"); + let res_json = &resp.json().expect("Could not get JSON result from distant node"); + let res_json: LookupResult = LookupResult(res_json); + let results = lookup2identities(&res_json); + let idty = match results.len() { + 0 => return Err(String::from("No matching identity found")), + 1 => &results[0], + _ => { + lookup_print(&res_json); + return Err(format!("Too much identities found ({})", results.len())) + }, + }; + + if idty.revoked { + return Err(format!("Identity {} {} is revoked and cannot be certified anymore.", idty.public, idty.uid)) + } + + println!("Get current block for blockstamp..."); + let current = crate::bma::blockchain::current(&node); + let currency = ¤t.currency; + let issuer = keyring.get_public_base58(); + let current_blockstamp = ¤t.blockstamp(); + + let membership = Membership { + version: 10, + currency: currency.clone(), + issuer, + idty_uid: (&idty).uid.clone(), + idty_blockstamp: (&idty).blockstamp.clone(), + adhere_blockstamp: current_blockstamp.clone(), + }; + let signature = keyring.sign(&membership); + crate::bma::wot::membership(&node, &membership.to_signable(), &signature.to_string()) +} \ No newline at end of file diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a6c4df6..311154d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -8,10 +8,15 @@ use crate::crypto::duniter_key::ScryptDuniterKey; use core::fmt; pub mod certify; +pub mod adhere; const DEFAULT_NODE: &str = "https://g1-test.duniter.org"; - -// TODO: constantize pub, sec, etc +const CMD_PUB: &str = "pub"; +const CMD_SEC: &str = "sec"; +const CMD_KEYRING: &str = "keyring"; +const CMD_LOOKUP: &str = "lookup"; +const CMD_CERTIFY: &str = "certify"; +const CMD_ADHERE: &str = "adhere"; pub enum Command { /// Compute the public key from salt/passwd and displays it @@ -24,6 +29,8 @@ pub enum Command { LOOKUP(String, BmaNode, String), /// Search an identity on a Duniter node (BMA API) and certify it CERTIFY(String, BmaNode, String), + /// Search an identity matching local pubkey on a Duniter node (BMA API) and send a membership based on it + ADHERE(String, BmaNode), /// Some unknown command UNKNOWN(String), } @@ -32,11 +39,12 @@ impl Command { pub fn name(&self) -> String { let str_name = match self { - PUB(_, _, _) => "pub", - SEC(_, _, _) => "sec", - KEYRING(_, _, _) => "keyring", - LOOKUP(_, _, _) => "lookup", - CERTIFY(_, _, _) => "certify", + PUB(..) => CMD_PUB, + SEC(..) => CMD_SEC, + KEYRING(..) => CMD_KEYRING, + LOOKUP(..) => CMD_LOOKUP, + CERTIFY(..) => CMD_CERTIFY, + ADHERE(..) => CMD_ADHERE, UNKNOWN(name) => name.as_str(), }; str_name.to_string() @@ -51,8 +59,8 @@ impl Command { KEYRING(_, salt, passwd) => println!("{}", compute_key(ScryptDuniterKey::new(salt.to_string(), passwd.to_string()))), LOOKUP(_, bma_node, uid_or_pub) => bma::lookup_identity::lookup(bma_node, uid_or_pub), CERTIFY(_, bma_node, uid_or_pub) => cli::certify::certify(bma_node, uid_or_pub).unwrap_or_else(|e| eprintln!("{}", e)), + ADHERE(_, bma_node) => cli::adhere::adhere(bma_node).unwrap_or_else(|e| eprintln!("{}", e)), UNKNOWN(cmd) => eprintln!("Unknown command {}", cmd), - _ => return Err("Not handled command yet".to_string()) } Ok(()) } @@ -67,33 +75,38 @@ impl Command { let command = command.as_str(); match command { - "pub" => { + CMD_PUB => { let salt = args.next().expect("Salt must be provided"); let passwd = args.next().expect("Password must be provided"); Some(PUB(command.to_string(), salt, passwd)) }, - "sec" => { + CMD_SEC => { let salt = args.next().expect("Salt must be provided"); let passwd = args.next().expect("Password must be provided"); Some(SEC(command.to_string(), salt, passwd)) }, - "keyring" => { + CMD_KEYRING => { let salt = args.next().expect("Salt must be provided"); let passwd = args.next().expect("Password must be provided"); Some(KEYRING(command.to_string(), salt, passwd)) }, - "lookup" => { + CMD_LOOKUP => { let uid_or_pub = args.next().expect("UID or pubkey must be provided"); let default = String::from(DEFAULT_NODE); let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str()); Some(LOOKUP(command.to_string(), bma_node, uid_or_pub)) }, - "cert" => { + CMD_CERTIFY => { let uid_or_pub = args.next().expect("UID or pubkey must be provided"); let default = String::from(DEFAULT_NODE); let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str()); Some(CERTIFY(command.to_string(), bma_node, uid_or_pub)) }, + CMD_ADHERE => { + let default = String::from(DEFAULT_NODE); + let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str()); + Some(ADHERE(command.to_string(), bma_node)) + }, _ => Some(UNKNOWN(command.to_string())), } } diff --git a/src/dubp/documents/membership.rs b/src/dubp/documents/membership.rs new file mode 100644 index 0000000..4d39f3a --- /dev/null +++ b/src/dubp/documents/membership.rs @@ -0,0 +1,23 @@ +#[derive(Debug)] +pub struct Membership { + pub version: u32, + pub currency: String, + pub issuer: String, + pub idty_uid: String, + pub idty_blockstamp: String, + pub adhere_blockstamp: String, +} + +impl crate::dubp::signable::Signable for Membership { + fn to_signable(self: &Self) -> String { + format!("Version: {} +Type: Membership +Currency: {} +Issuer: {} +Block: {} +Membership: IN +UserID: {} +CertTS: {} +", self.version, self.currency, self.issuer, self.adhere_blockstamp, self.idty_uid, self.idty_blockstamp) + } +} \ No newline at end of file diff --git a/src/dubp/documents/mod.rs b/src/dubp/documents/mod.rs index 17d2a22..891883c 100644 --- a/src/dubp/documents/mod.rs +++ b/src/dubp/documents/mod.rs @@ -1 +1,2 @@ -pub mod certification; \ No newline at end of file +pub mod certification; +pub mod membership; \ No newline at end of file -- GitLab