diff --git a/network-documents/src/network_documents.pest b/network-documents/src/network_documents.pest index 9cf01d409d8f1cd3bd22670ac172cb0fcf6e73cd..5461807299b8949616d2d5c7312cd65de0d80761 100644 --- a/network-documents/src/network_documents.pest +++ b/network-documents/src/network_documents.pest @@ -78,19 +78,22 @@ peer_v11 = ${ "11:" ~ currency ~ ":" ~ node_id ~ ":" ~ pubkey ~ ":" ~ blockstamp api_outgoing_conf = @{ u8 } api_incoming_conf = @{ u8 } free_member_rooms = @{ u8 } -low_priority_rooms = @{ u8 } +free_mirror_rooms = @{ u8 } software = @{ ASCII_ALPHA ~ ASCII_ALPHANUMERIC* } -soft_version = @{ ASCII_DIGIT ~ "." ~ ASCII_DIGIT ~ "." ~ ASCII_DIGIT ~ ("-" ~ ("a" | "b" | "rc") ~ ASCII_DIGIT ~ (ASCII_DIGIT ~ "."){0,2})? } +soft_version = @{ ASCII_DIGIT ~ "." ~ ASCII_DIGIT ~ "." ~ ASCII_DIGIT ~ ("-" ~ ("a" | "b" | "rc") ~ ASCII_DIGIT ~ ("." ~ ASCII_DIGIT){0,2})? } +step = @{ u_int } -head_v3_body = ${ +head_v3 = ${ + "3:" ~ currency ~ ":" ~ api_outgoing_conf ~ ":" ~ api_incoming_conf ~ ":" ~ free_member_rooms ~ ":" ~ - low_priority_rooms ~ ":" ~ + free_mirror_rooms ~ ":" ~ node_id ~ ":" ~ pubkey ~ ":" ~ blockstamp ~ ":" ~ software ~ ":" ~ - soft_version + soft_version ~ nl ~ + ed25519_sig? ~ + (nl ~ step)? } - diff --git a/network-documents/src/network_head.rs b/network-documents/src/network_head.rs index 5f6d2979299109836ba086004bad7243efd54622..396e977ce73795786dafd2920957ef0d5ce06ce0 100644 --- a/network-documents/src/network_head.rs +++ b/network-documents/src/network_head.rs @@ -32,7 +32,7 @@ pub enum NetworkHead { /// Head V2 V2(Box<NetworkHeadV2>), /// head V3 - V3(Box<NetworkHeadV3Container>), + V3(Box<NetworkHeadV3>), } impl ToString for NetworkHead { diff --git a/network-documents/src/network_head_v3.rs b/network-documents/src/network_head_v3.rs index 25251ff5334093f41bcf449b13db563c6f62c27e..e9998fe2db72f1a68f5e5ac0ed7828dc92b7c15d 100644 --- a/network-documents/src/network_head_v3.rs +++ b/network-documents/src/network_head_v3.rs @@ -17,68 +17,27 @@ use base58::ToBase58; use duniter_documents::blockstamp::Blockstamp; -use dup_crypto::keys::bin_signable::BinSignable; +use duniter_documents::{BlockHash, BlockId, CurrencyName, ToJsonObject}; +use dup_crypto::keys::text_signable::TextSignable; use dup_crypto::keys::*; -use serde_json; +use pest::iterators::Pair; +use pest::Parser; use std::cmp::Ordering; -use NodeId; - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -/// Head V3 -pub struct NetworkHeadV3Container { - /// Head step - pub step: u8, - /// head body - pub body: NetworkHeadV3, -} - -impl PartialOrd for NetworkHeadV3Container { - fn partial_cmp(&self, other: &NetworkHeadV3Container) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for NetworkHeadV3Container { - fn cmp(&self, other: &NetworkHeadV3Container) -> Ordering { - self.body.cmp(&other.body) - } -} - -impl NetworkHeadV3Container { - /// Convert to JSON String - pub fn to_json_head(&self) -> Result<String, serde_json::Error> { - Ok(serde_json::to_string_pretty(&JsonHeadV3 { - api_outgoing_conf: self.body.api_outgoing_conf, - api_incoming_conf: self.body.api_incoming_conf, - free_mirror_rooms: self.body.free_mirror_rooms, - low_priority_rooms: self.body.low_priority_rooms, - node_id: self.body.node_id, - algorithm: self.body.pubkey.algo(), - pubkey: self.body.pubkey.to_base58(), - blockstamp: self.body.blockstamp.to_string(), - software: &self.body.software, - soft_version: &self.body.soft_version, - signature: if let Some(sig) = self.body.signature { - Some(sig.to_base64()) - } else { - None - }, - step: self.step, - })?) - } -} +use *; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] /// Head V3 pub struct NetworkHeadV3 { + /// Currency name + pub currency_name: CurrencyName, /// WS2P Private configuration pub api_outgoing_conf: u8, /// WS2P Public configuration pub api_incoming_conf: u8, + /// Issuer node free member rooms + pub free_member_rooms: u8, /// Issuer node free mirror rooms pub free_mirror_rooms: u8, - /// Issuer node free "low priority" rooms - pub low_priority_rooms: u8, /// Issuer node id pub node_id: NodeId, /// Issuer pubkey @@ -91,6 +50,8 @@ pub struct NetworkHeadV3 { pub soft_version: String, /// Issuer signature pub signature: Option<Sig>, + /// Head step + pub step: u8, } impl PartialOrd for NetworkHeadV3 { @@ -105,7 +66,7 @@ impl Ord for NetworkHeadV3 { } } -impl<'de> BinSignable<'de> for NetworkHeadV3 { +/*impl<'de> BinSignable<'de> for NetworkHeadV3 { fn issuer_pubkey(&self) -> PubKey { self.pubkey } @@ -115,19 +76,122 @@ impl<'de> BinSignable<'de> for NetworkHeadV3 { fn set_signature(&mut self, signature: Sig) { self.signature = Some(signature); } +}*/ + +impl TextSignable for NetworkHeadV3 { + fn as_signable_text(&self) -> String { + format!( +"3:{currency}:{api_outgoing_conf}:{api_incoming_conf}:{free_member_rooms}:{free_mirror_rooms}:{node_id}:{pubkey}:{blockstamp}:{software}:{soft_version}\n", + currency = self.currency_name, + api_outgoing_conf = self.api_outgoing_conf, + api_incoming_conf = self.api_incoming_conf, + free_member_rooms = self.free_member_rooms, + free_mirror_rooms = self.free_mirror_rooms, + node_id = format!("{}", self.node_id), + pubkey = self.pubkey.to_base58(), + blockstamp = self.blockstamp.to_string(), + software = self.software, + soft_version = self.soft_version, + ) + } + fn issuer_pubkey(&self) -> PubKey { + self.pubkey + } + fn signature(&self) -> Option<Sig> { + self.signature + } + fn set_signature(&mut self, signature: Sig) { + self.signature = Some(signature); + } +} + +impl NetworkHeadV3 { + /// parse from raw ascii format + pub fn parse_from_raw(raw_peer: &str) -> Result<NetworkHeadV3, ParseError> { + match NetworkDocsParser::parse(Rule::head_v3, raw_peer) { + Ok(mut head_v3_pairs) => { + Ok(NetworkHeadV3::from_pest_pair(head_v3_pairs.next().unwrap())) + } + Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))), + } + } + /// Generate from pest pair + fn from_pest_pair(pair: Pair<Rule>) -> NetworkHeadV3 { + let mut currency_str = ""; + let mut api_outgoing_conf = 0; + let mut api_incoming_conf = 0; + let mut free_member_rooms = 0; + let mut free_mirror_rooms = 0; + let mut node_id = NodeId(0); + let mut pubkey = None; + let mut blockstamp = None; + let mut software = ""; + let mut soft_version = ""; + let mut signature = None; + let mut step = 0; + for field in pair.into_inner() { + match field.as_rule() { + Rule::currency => currency_str = field.as_str(), + Rule::api_outgoing_conf => api_outgoing_conf = field.as_str().parse().unwrap(), + Rule::api_incoming_conf => api_incoming_conf = field.as_str().parse().unwrap(), + Rule::free_member_rooms => free_member_rooms = field.as_str().parse().unwrap(), + Rule::free_mirror_rooms => free_mirror_rooms = field.as_str().parse().unwrap(), + Rule::node_id => node_id = NodeId(field.as_str().parse().unwrap()), + Rule::pubkey => { + pubkey = Some(PubKey::Ed25519( + ed25519::PublicKey::from_base58(field.as_str()).unwrap(), + )) + } + Rule::blockstamp => { + let mut inner_rules = field.into_inner(); // { block_id ~ "-" ~ hash } + + let block_id: &str = inner_rules.next().unwrap().as_str(); + let block_hash: &str = inner_rules.next().unwrap().as_str(); + blockstamp = Some(Blockstamp { + id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string. + hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string. + }); + } + Rule::software => software = field.as_str(), + Rule::soft_version => soft_version = field.as_str(), + Rule::ed25519_sig => { + signature = Some(Sig::Ed25519( + ed25519::Signature::from_base64(field.as_str()).unwrap(), + )) + } + Rule::step => step = field.as_str().parse().unwrap(), + _ => panic!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line + } + } + NetworkHeadV3 { + currency_name: CurrencyName(currency_str.to_owned()), + api_outgoing_conf, + api_incoming_conf, + free_member_rooms, + free_mirror_rooms, + node_id, + pubkey: pubkey.expect("Grammar must ensure that head v3 have valid issuer pubkey !"), + blockstamp: blockstamp + .expect("Grammar must ensure that head v3 have valid blockstamp!"), + software: software.to_owned(), + soft_version: soft_version.to_owned(), + signature, + step, + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] /// Head V3 for json serializer -pub struct JsonHeadV3<'a> { +pub struct JsonHeadV3 { /// WS2P Private configuration pub api_outgoing_conf: u8, /// WS2P Public configuration pub api_incoming_conf: u8, + /// Issuer node free member rooms + pub free_member_rooms: u8, /// Issuer node free mirror rooms pub free_mirror_rooms: u8, - /// Issuer node free "low priority" rooms - pub low_priority_rooms: u8, /// Issuer node id pub node_id: NodeId, /// Issuer key algorithm @@ -137,57 +201,77 @@ pub struct JsonHeadV3<'a> { /// Head blockstamp pub blockstamp: String, /// Issuer node software - pub software: &'a str, + pub software: String, /// Issuer node soft version - pub soft_version: &'a str, + pub soft_version: String, /// Issuer signature pub signature: Option<String>, /// Head step pub step: u8, } +impl<'a> ToJsonObject for NetworkHeadV3 { + type JsonObject = JsonHeadV3; + + fn to_json_object(&self) -> Self::JsonObject { + JsonHeadV3 { + api_outgoing_conf: self.api_outgoing_conf, + api_incoming_conf: self.api_incoming_conf, + free_member_rooms: self.free_member_rooms, + free_mirror_rooms: self.free_mirror_rooms, + node_id: self.node_id, + algorithm: self.pubkey.algo(), + pubkey: self.pubkey.to_base58(), + blockstamp: self.blockstamp.to_string(), + software: self.software.clone(), + soft_version: self.soft_version.clone(), + signature: if let Some(sig) = self.signature { + Some(sig.to_base64()) + } else { + None + }, + step: self.step, + } + } +} + #[cfg(test)] mod tests { use super::*; - use dup_crypto::keys::bin_signable::BinSignable; - use tests::bincode::deserialize; use tests::keypair1; #[test] fn head_v3_sign_and_verify() { - let mut head_v3 = NetworkHeadV3Container { + let keypair = keypair1(); + let mut head_v3 = NetworkHeadV3 { + currency_name: CurrencyName("g1".to_owned()), + api_outgoing_conf: 0u8, + api_incoming_conf: 0u8, + free_mirror_rooms: 0u8, + free_member_rooms: 0u8, + node_id: NodeId(0), + pubkey: PubKey::Ed25519(keypair.public_key()), + blockstamp: Blockstamp::from_string( + "50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370", + ) + .unwrap(), + software: String::from("durs"), + soft_version: String::from("0.1.0-a0.1"), + signature: None, step: 0, - body: NetworkHeadV3 { - api_outgoing_conf: 0u8, - api_incoming_conf: 0u8, - free_mirror_rooms: 0u8, - low_priority_rooms: 0u8, - node_id: NodeId(0), - pubkey: PubKey::Ed25519(keypair1().public_key()), - blockstamp: Blockstamp::from_string( - "50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370", - ) - .unwrap(), - software: String::from("durs"), - soft_version: String::from("0.1.0-a0.1"), - signature: None, - }, }; // Sign - let sign_result = head_v3 - .body - .sign(PrivKey::Ed25519(keypair1().private_key())); - if let Ok(head_v3_body_bytes) = sign_result { - let deser_head_v3_body: NetworkHeadV3 = - deserialize(&head_v3_body_bytes).expect("Fail to deserialize PeerCardV11 !"); - assert_eq!(head_v3.body, deser_head_v3_body,) + let sign_result = head_v3.sign(PrivKey::Ed25519(keypair.private_key())); + if let Ok(head_v3_raw) = sign_result { + println!("{}", head_v3_raw); + assert_eq!( + head_v3, + NetworkHeadV3::parse_from_raw(&head_v3_raw).expect("Fail to parse head v3 !") + ) } else { - panic!("failt to sign head v3 : {:?}", sign_result.err().unwrap()) + panic!("fail to sign head v3 : {:?}", sign_result.err().unwrap()) } // Verify signature - head_v3.body.verify().expect("HEADv3 : Invalid signature !"); - //let json_head_v3 = head_v3.to_json_head().expect("Fail to serialize HEAD v3 !"); - //println!("{}", json_head_v3); - //panic!(); + head_v3.verify().expect("HEADv3 : Invalid signature !"); } } diff --git a/ws2p-messages/v2/payload_container.rs b/ws2p-messages/v2/payload_container.rs index af62ee4d93eb5a9158bbbffe6d03e80d8ca11cf9..8bf5cfd89778dfb5a8a5a9477edb8d6686ab80f8 100644 --- a/ws2p-messages/v2/payload_container.rs +++ b/ws2p-messages/v2/payload_container.rs @@ -26,7 +26,7 @@ use duniter_documents::v10::revocation::RevocationDocument; use duniter_documents::v10::transaction::TransactionDocument; use dup_crypto::hashs::Hash; use durs_network_documents::network_head_v2::NetworkHeadV2; -use durs_network_documents::network_head_v3::NetworkHeadV3Container; +use durs_network_documents::network_head_v3::NetworkHeadV3; use durs_network_documents::network_peer::PeerCardV11; /// WS2P v2 message payload metadata size @@ -54,7 +54,7 @@ pub enum WS2Pv0MessagePayload { /// HEADS_V2 Message Headsv2(Vec<NetworkHeadV2>), /// HEADS_V3 Message - Heads3(Vec<NetworkHeadV3Container>), + Heads3(Vec<NetworkHeadV3>), /// BLOCKS Message Blocks(Vec<BlockDocument>), /// PENDING_IDENTITIES Message