use crate::network_endpoint::*;
use crate::*;
use base58::ToBase58;
use dubp_documents::ToStringObject;
use dubp_documents::{blockstamp::Blockstamp, CurrencyName};
use dubp_documents::{BlockHash, BlockId};
use dup_crypto::keys::text_signable::TextSignable;
use dup_crypto::keys::*;
use pest::iterators::Pair;
use pest::Parser;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PeerCardV10 {
pub blockstamp: Blockstamp,
pub issuer: PubKey,
pub endpoints: Vec<EndpointEnum>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PeerCardV11 {
pub currency_name: CurrencyName,
pub issuer: PubKey,
pub node_id: NodeId,
pub blockstamp: Blockstamp,
pub endpoints: Vec<EndpointV2>,
pub endpoints_str: Vec<String>,
pub sig: Option<Sig>,
}
#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
pub struct PeerCardV11Stringified {
pub currency_name: String,
pub issuer: String,
pub node_id: String,
pub blockstamp: String,
pub endpoints: Vec<String>,
pub sig: String,
}
impl ToStringObject for PeerCardV11 {
type StringObject = PeerCardV11Stringified;
fn to_string_object(&self) -> PeerCardV11Stringified {
let mut endpoints: Vec<String> = self.endpoints.iter().map(EndpointV2::to_string).collect();
endpoints.extend_from_slice(&self.endpoints_str);
PeerCardV11Stringified {
currency_name: self.currency_name.0.clone(),
issuer: format!("{}", self.issuer),
node_id: format!("{}", self.node_id),
blockstamp: format!("{}", self.blockstamp),
endpoints,
sig: if let Some(sig) = self.sig {
format!("{}", sig)
} else {
"".to_owned()
},
}
}
}
impl TextSignable for PeerCardV11 {
fn as_signable_text(&self) -> String {
format!(
"11:{currency}:{node_id}:{pubkey}:{blockstamp}\n{endpoinds}\n{endpoints_str}{nl}",
currency = self.currency_name.0,
node_id = format!("{}", self.node_id),
pubkey = self.issuer.to_base58(),
blockstamp = self.blockstamp.to_string(),
endpoinds = self
.endpoints
.iter()
.map(EndpointV2::to_string)
.collect::<Vec<String>>()
.join("\n"),
endpoints_str = self.endpoints_str.join("\n"),
nl = if self.endpoints_str.is_empty() {
""
} else {
"\n"
},
)
}
fn issuer_pubkey(&self) -> PubKey {
self.issuer
}
fn signature(&self) -> Option<Sig> {
self.sig
}
fn set_signature(&mut self, signature: Sig) {
self.sig = Some(signature);
}
}
impl TextDocumentParser<Rule> for PeerCardV11 {
type DocumentType = PeerCardV11;
fn parse(doc: &str) -> Result<Self::DocumentType, TextDocumentParseError> {
match NetworkDocsParser::parse(Rule::peer_v11, doc) {
Ok(mut peer_v11_pairs) => {
Ok(PeerCardV11::from_pest_pair(peer_v11_pairs.next().unwrap()))
}
Err(pest_error) => Err(TextDocumentParseError::PestError(format!("{}", pest_error))),
}
}
fn from_pest_pair(pair: Pair<Rule>) -> PeerCardV11 {
let mut currency_str = "";
let mut node_id = NodeId(0);
let mut issuer = None;
let mut blockstamp = None;
let mut endpoints = Vec::new();
let mut sig = None;
for field in pair.into_inner() {
match field.as_rule() {
Rule::currency => currency_str = field.as_str(),
Rule::node_id => node_id = NodeId(field.as_str().parse().unwrap()),
Rule::pubkey => {
issuer = Some(PubKey::Ed25519(
ed25519::PublicKey::from_base58(field.as_str()).unwrap(),
))
}
Rule::blockstamp => {
let mut inner_rules = field.into_inner();
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()),
hash: BlockHash(Hash::from_hex(block_hash).unwrap()),
});
}
Rule::endpoint_v2 => endpoints.push(EndpointV2::from_pest_pair(field)),
Rule::ed25519_sig => {
sig = Some(Sig::Ed25519(
ed25519::Signature::from_base64(field.as_str()).unwrap(),
))
}
_ => panic!("unexpected rule: {:?}", field.as_rule()),
}
}
let endpoints_len = endpoints.len();
PeerCardV11 {
currency_name: CurrencyName(currency_str.to_owned()),
issuer: issuer.expect("Grammar must ensure that peer v11 have valid issuer pubkey !"),
node_id,
blockstamp: blockstamp
.expect("Grammar must ensure that peer v11 have valid blockstamp!"),
endpoints,
endpoints_str: Vec::with_capacity(endpoints_len),
sig,
}
}
}
impl PeerCardV11 {
pub fn to_json_peer(&self) -> Result<String, serde_json::Error> {
Ok(serde_json::to_string_pretty(&JsonPeerCardV11 {
version: 11,
currency_name: &self.currency_name.0,
node_id: self.node_id,
algorithm: self.issuer.algo(),
pubkey: self.issuer.to_base58(),
blockstamp: self.blockstamp.to_string(),
endpoints: self.endpoints.iter().map(|ep| ep.to_string()).collect(),
signature: if let Some(sig) = self.sig {
Some(sig.to_base64())
} else {
None
},
})?)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct JsonPeerCardV11<'a> {
pub version: usize,
pub currency_name: &'a str,
pub node_id: NodeId,
pub algorithm: KeysAlgo,
pub pubkey: String,
pub blockstamp: String,
pub endpoints: Vec<String>,
pub signature: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PeerCard {
V10(PeerCardV10),
V11(PeerCardV11),
}
impl PeerCard {
pub fn version(&self) -> u32 {
match *self {
PeerCard::V10(ref _peer_v10) => 10,
PeerCard::V11(ref _peer_v11) => 11,
}
}
pub fn blockstamp(&self) -> Blockstamp {
match *self {
PeerCard::V10(ref peer_v10) => peer_v10.blockstamp,
PeerCard::V11(ref peer_v11) => peer_v11.blockstamp,
}
}
pub fn issuer(&self) -> PubKey {
match *self {
PeerCard::V10(ref peer_v10) => peer_v10.issuer,
PeerCard::V11(ref peer_v11) => peer_v11.issuer,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::keypair1;
use std::net::Ipv4Addr;
use std::str::FromStr;
fn create_endpoint_v2() -> EndpointV2 {
EndpointV2 {
api: NetworkEndpointApi(String::from("WS2P")),
api_version: 2,
network_features: EndpointV2NetworkFeatures(vec![1u8]),
api_features: ApiFeatures(vec![7u8]),
ip_v4: None,
ip_v6: None,
host: Some(String::from("g1.durs.ifee.fr")),
port: 443u16,
path: Some(String::from("ws2p")),
}
}
fn create_second_endpoint_v2() -> EndpointV2 {
EndpointV2 {
api: NetworkEndpointApi(String::from("WS2P")),
api_version: 2,
network_features: EndpointV2NetworkFeatures(vec![1u8]),
api_features: ApiFeatures(vec![7u8]),
ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
ip_v6: None,
host: None,
port: 443u16,
path: Some(String::from("ws2p")),
}
}
#[test]
fn peer_card_v11_sign_and_verify() {
let keypair1 = keypair1();
let mut peer_card_v11 = PeerCardV11 {
currency_name: CurrencyName(String::from("g1")),
issuer: PubKey::Ed25519(keypair1.public_key()),
node_id: NodeId(0),
blockstamp: Blockstamp::from_string(
"50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370",
)
.unwrap(),
endpoints: vec![create_endpoint_v2(), create_second_endpoint_v2()],
endpoints_str: vec![],
sig: None,
};
let sign_result = peer_card_v11.sign(PrivKey::Ed25519(keypair1.private_key()));
if let Ok(peer_card_v11_raw) = sign_result {
println!("{}", peer_card_v11_raw);
assert_eq!(
peer_card_v11,
PeerCardV11::parse(&peer_card_v11_raw).expect("Fail to parse peer card v11 !")
)
} else {
panic!("fail to sign peer card : {:?}", sign_result.err().unwrap())
}
peer_card_v11
.verify()
.expect("Fail to verify PeerCardV11 !");
}
}