Skip to content
Snippets Groups Projects
Commit 7c40cda4 authored by Éloïs's avatar Éloïs
Browse files

[enh] #68 add WS2P crate

parent 9b098712
Branches
Tags
1 merge request!58Resolve "Add crates blockchain, conf, core, dal, message, module, network, tui and ws2p"
[package]
name = "duniter-ws2p"
version = "0.1.0"
authors = ["librelois <elois@ifee.fr>"]
description = "WebSocketToPeer API for the Duniter project."
license = "AGPL-3.0"
[lib]
path = "lib.rs"
[dependencies]
duniter-conf = { path = "../conf" }
duniter-crypto = { path = "../crypto" }
duniter-dal = { path = "../dal" }
duniter-documents = { path = "../documents" }
duniter-message = { path = "../message" }
duniter-module = { path = "../module" }
duniter-network = { path = "../network" }
duniter-wotb = { path = "../wotb" }
lazy_static = "1.0.0"
log = "0.4.1"
rand = "0.4.2"
regex = "0.2.6"
rust-crypto = "0.2.36"
sqlite = "0.23.9"
serde = "1.0.24"
serde_derive = "1.0.24"
serde_json = "1.0.9"
websocket = "0.20.2"
\ No newline at end of file
extern crate duniter_crypto;
extern crate serde;
extern crate serde_json;
use self::serde::ser::{Serialize, SerializeStruct, Serializer};
use super::WS2PMessage;
use duniter_crypto::keys::ed25519::PublicKey as ed25519PublicKey;
use duniter_crypto::keys::PublicKey;
#[derive(Debug, Clone)]
pub struct WS2PAckMessageV1 {
pub currency: String,
pub pubkey: ed25519PublicKey,
pub challenge: String,
pub signature: Option<duniter_crypto::keys::ed25519::Signature>,
}
impl WS2PMessage for WS2PAckMessageV1 {
fn parse(v: &serde_json::Value, currency: String) -> Option<Self> {
let pubkey = match v.get("pub") {
Some(pubkey) => pubkey.as_str().unwrap().to_string(),
None => return None,
};
let signature = match v.get("sig") {
Some(signature) => signature.as_str().unwrap().to_string(),
None => return None,
};
let pubkey: ed25519PublicKey = ed25519PublicKey::from_base58(&pubkey).unwrap();
let signature: Option<duniter_crypto::keys::ed25519::Signature> =
Some(duniter_crypto::keys::Signature::from_base64(&signature).unwrap());
Some(WS2PAckMessageV1 {
currency,
pubkey,
challenge: "".to_string(),
signature,
})
}
fn to_raw(&self) -> String {
format!(
"WS2P:ACK:{}:{}:{}",
self.currency, self.pubkey, self.challenge
)
}
fn verify(&self) -> bool {
self.pubkey
.verify(self.to_raw().as_bytes(), &self.signature.unwrap())
}
}
impl Serialize for WS2PAckMessageV1 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut connect_message_in_json = serializer.serialize_struct("message", 3)?;
connect_message_in_json.serialize_field("auth", "ACK")?;
connect_message_in_json.serialize_field("pub", &self.pubkey.to_string())?;
connect_message_in_json.serialize_field(
"sig",
&self
.signature
.expect("Fail to serialize ACK message : the signature field is set to None !")
.to_string(),
)?;
connect_message_in_json.end()
}
}
extern crate duniter_crypto;
extern crate serde;
extern crate serde_json;
use self::serde::ser::{Serialize, SerializeStruct, Serializer};
use super::WS2PMessage;
use duniter_crypto::keys::ed25519::PublicKey as ed25519PublicKey;
use duniter_crypto::keys::PublicKey;
#[derive(Debug, Clone)]
pub struct WS2PConnectMessageV1 {
pub currency: String,
pub pubkey: ed25519PublicKey,
pub challenge: String,
pub signature: Option<duniter_crypto::keys::ed25519::Signature>,
}
impl WS2PMessage for WS2PConnectMessageV1 {
fn parse(v: &serde_json::Value, currency: String) -> Option<Self> {
let pubkey = match v.get("pub") {
Some(pubkey) => pubkey.as_str().unwrap().to_string(),
None => return None,
};
let challenge = match v.get("challenge") {
Some(challenge) => challenge.as_str().unwrap().to_string(),
None => return None,
};
let signature = match v.get("sig") {
Some(signature) => signature.as_str().unwrap().to_string(),
None => return None,
};
let pubkey: ed25519PublicKey = ed25519PublicKey::from_base58(&pubkey).unwrap();
let signature: Option<duniter_crypto::keys::ed25519::Signature> =
Some(duniter_crypto::keys::Signature::from_base64(&signature).unwrap());
Some(WS2PConnectMessageV1 {
currency,
pubkey,
challenge,
signature,
})
}
fn to_raw(&self) -> String {
format!(
"WS2P:CONNECT:{}:{}:{}",
self.currency, self.pubkey, self.challenge
)
}
fn verify(&self) -> bool {
self.pubkey
.verify(self.to_raw().as_bytes(), &self.signature.unwrap())
}
/*fn parse_and_verify(v: serde_json::Value, currency: String) -> bool {
let pubkey = match v.get("pub") {
Some(pubkey) => pubkey.as_str().unwrap().to_string(),
None => return false,
};
let challenge = match v.get("pub") {
Some(challenge) => challenge.as_str().unwrap().to_string(),
None => return false,
};
let signature = match v.get("pub") {
Some(signature) => signature.as_str().unwrap().to_string(),
None => return false,
};
ed25519PublicKey::from_base58(&pubkey).unwrap().verify(format!("WS2P:CONNECT:{}:{}:{}", currency, pubkey, challenge),&duniter_keys::Signature::from_base64(&signature).unwrap())
}*/
}
impl Serialize for WS2PConnectMessageV1 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut connect_message_in_json = serializer.serialize_struct("message", 4)?;
connect_message_in_json.serialize_field("auth", "CONNECT")?;
connect_message_in_json.serialize_field("pub", &self.pubkey.to_string())?;
connect_message_in_json.serialize_field("challenge", &self.challenge)?;
connect_message_in_json.serialize_field(
"sig",
&self
.signature
.expect("Fail to serialize CONNECT message : the signature field is set to None !")
.to_string(),
)?;
connect_message_in_json.end()
}
}
extern crate regex;
use self::regex::Regex;
lazy_static! {
#[derive(Debug)]
pub static ref WS2P_V1_ENDPOINT_REGEX: Regex = Regex::new(
"^WS2P (?P<version>[1-9][0-9]* )?(?P<uuid>[a-f0-9]{6,8}) (?P<host>[a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) (?P<port>[0-9]+)(?: /?(?P<path>.+)?)? *$"
).unwrap();
}
pub static WS2P_OUTCOMING_INTERVAL_AT_STARTUP: &'static u64 = &75;
pub static WS2P_OUTCOMING_INTERVAL: &'static u64 = &300;
pub static WS2P_DEFAULT_OUTCOMING_QUOTA: &'static usize = &10;
pub static WS2P_NEGOTIATION_TIMEOUT: &'static u64 = &15;
//pub static WS2P_REQUEST_TIMEOUT : &'static u64 = &30;
pub static WS2P_CONNECTION_TIMEOUT: &'static u64 = &120;
pub static WS2P_SPAM_INTERVAL_IN_MILLI_SECS: &'static u64 = &80;
pub static WS2P_SPAM_LIMIT: &'static u64 = &6;
pub static WS2P_SPAM_SLEEP_TIME_IN_SEC: &'static u64 = &100;
pub static DURATION_BEFORE_RECORDING_ENDPOINT: &'static u64 = &180;
pub static BLOCKS_REQUEST_INTERVAL: &'static u64 = &60;
pub static PENDING_IDENTITIES_REQUEST_INTERVAL: &'static u64 = &40;
ws2p/lib.rs 0 → 100644
This diff is collapsed.
extern crate duniter_crypto;
extern crate serde;
extern crate serde_json;
use self::serde::ser::{Serialize, SerializeStruct, Serializer};
use super::WS2PMessage;
use duniter_crypto::keys::ed25519::PublicKey as ed25519PublicKey;
use duniter_crypto::keys::PublicKey;
#[derive(Debug, Clone)]
pub struct WS2POkMessageV1 {
pub currency: String,
pub pubkey: ed25519PublicKey,
pub challenge: String,
pub signature: Option<duniter_crypto::keys::ed25519::Signature>,
}
impl WS2PMessage for WS2POkMessageV1 {
fn parse(v: &serde_json::Value, currency: String) -> Option<Self> {
let signature = match v.get("sig") {
Some(signature) => signature
.as_str()
.expect("Parsing of OK message : fail to convert sig to str")
.to_string(),
None => return None,
};
let pubkey: ed25519PublicKey = ed25519PublicKey::from_base58(
"969qRJs8KhsnkyzqarpL4RKZGMdVKNbZgu8fhsigM7Lj",
).expect("fail to create default pubkey !");
let signature: Option<duniter_crypto::keys::ed25519::Signature> = Some(
duniter_crypto::keys::Signature::from_base64(&signature)
.expect("fail to parse signature of OK message !"),
);
Some(WS2POkMessageV1 {
currency,
pubkey,
challenge: "".to_string(),
signature,
})
}
fn to_raw(&self) -> String {
format!(
"WS2P:OK:{}:{}:{}",
self.currency, self.pubkey, self.challenge
)
}
fn verify(&self) -> bool {
self.pubkey
.verify(self.to_raw().as_bytes(), &self.signature.unwrap())
}
}
impl Serialize for WS2POkMessageV1 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut connect_message_in_json = serializer.serialize_struct("message", 2)?;
connect_message_in_json.serialize_field("auth", "OK")?;
connect_message_in_json.serialize_field(
"sig",
&self
.signature
.expect("Fail to serialize OK message : the signature field is set to None !")
.to_string(),
)?;
connect_message_in_json.end()
}
}
File added
extern crate serde_json;
extern crate websocket;
use duniter_crypto::keys::ed25519;
use duniter_crypto::keys::PublicKey;
use duniter_dal::parsers::blocks::parse_json_block;
use duniter_module::ModuleReqId;
use duniter_network::network_endpoint::{NetworkEndpoint, NetworkEndpointApi};
use duniter_network::{NetworkDocument, NodeUUID};
use std::fmt::Debug;
use std::net::TcpStream;
use super::{NodeFullId, WS2PAckMessageV1, WS2PConnectMessageV1, WS2PMessage, WS2POkMessageV1};
#[derive(Debug, Copy, Clone)]
pub enum WS2POrderForListeningThread {
Close,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum WS2PConnectionState {
NeverTry = 0,
TryToOpenWS = 1,
WSError = 2,
TryToSendConnectMess = 3,
Unreachable = 4,
WaitingConnectMess = 5,
NoResponse = 6,
ConnectMessOk = 7,
OkMessOkWaitingAckMess = 8,
AckMessOk = 9,
Denial = 10,
Close = 11,
Established = 12,
}
impl From<u32> for WS2PConnectionState {
fn from(integer: u32) -> Self {
match integer {
1 | 2 => WS2PConnectionState::WSError,
3 | 4 => WS2PConnectionState::Unreachable,
5 | 6 => WS2PConnectionState::NoResponse,
7 | 8 | 9 | 10 => WS2PConnectionState::Denial,
11 | 12 => WS2PConnectionState::Close,
_ => WS2PConnectionState::NeverTry,
}
}
}
impl WS2PConnectionState {
pub fn from_u32(integer: u32, from_db: bool) -> Self {
if from_db {
WS2PConnectionState::from(integer)
} else {
match integer {
1 => WS2PConnectionState::TryToOpenWS,
2 => WS2PConnectionState::WSError,
3 | 4 => WS2PConnectionState::Unreachable,
5 | 6 => WS2PConnectionState::NoResponse,
7 => WS2PConnectionState::ConnectMessOk,
8 => WS2PConnectionState::OkMessOkWaitingAckMess,
9 => WS2PConnectionState::AckMessOk,
10 => WS2PConnectionState::Denial,
11 => WS2PConnectionState::Close,
12 => WS2PConnectionState::Established,
_ => WS2PConnectionState::NeverTry,
}
}
}
pub fn to_u32(&self) -> u32 {
match *self {
WS2PConnectionState::NeverTry => 0,
_ => 1,
}
}
}
pub struct WebsocketSender(pub websocket::sender::Writer<TcpStream>);
impl Debug for WebsocketSender {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "WebsocketSender {{ }}")
}
}
#[derive(Debug)]
pub enum WS2PConnectionMessagePayload {
FailOpenWS,
WrongUrl,
FailToSplitWS,
TryToSendConnectMess,
FailSendConnectMess,
WebsocketOk(WebsocketSender),
NegociationTimeout,
ValidConnectMessage(String, WS2PConnectionState),
ValidAckMessage(String, WS2PConnectionState),
ValidOk(WS2PConnectionState),
DalRequest(ModuleReqId, serde_json::Value),
PeerCard(serde_json::Value, Vec<NetworkEndpoint>),
Heads(Vec<serde_json::Value>),
Document(NetworkDocument),
ReqResponse(ModuleReqId, serde_json::Value),
InvalidMessage,
WrongFormatMessage,
UnknowMessage,
Timeout,
Close,
}
#[derive(Debug)]
pub struct WS2PConnectionMessage(pub NodeFullId, pub WS2PConnectionMessagePayload);
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum WS2PCloseConnectionReason {
AuthMessInvalidSig,
NegociationTimeout,
Timeout,
Unknow,
}
#[derive(Debug, Clone)]
pub struct WS2PConnectionMetaData {
pub state: WS2PConnectionState,
pub remote_uuid: Option<NodeUUID>,
pub remote_pubkey: Option<ed25519::PublicKey>,
pub challenge: String,
pub remote_challenge: String,
pub current_blockstamp: Option<(u32, String)>,
}
#[derive(Debug, Clone)]
pub struct WS2PDatasForListeningThread {
pub conn_meta_datas: WS2PConnectionMetaData,
pub currency: String,
pub key_pair: ed25519::KeyPair,
}
impl WS2PConnectionMetaData {
pub fn new(challenge: String) -> Self {
WS2PConnectionMetaData {
state: WS2PConnectionState::WaitingConnectMess,
remote_uuid: None,
remote_pubkey: None,
challenge,
remote_challenge: "".to_string(),
current_blockstamp: None,
}
}
pub fn node_full_id(&self) -> NodeFullId {
NodeFullId(
self.clone()
.remote_uuid
.expect("Fail to get NodeFullId : remote_uuid is None !"),
self.remote_pubkey
.expect("Fail to get NodeFullId : remote_pubkey is None !"),
)
}
pub fn parse_and_check_incoming_message(
&mut self,
currency: &str,
key_pair: ed25519::KeyPair,
m: &serde_json::Value,
) -> WS2PConnectionMessagePayload {
if let Some(s) = m.get("auth") {
if s.is_string() {
match s.as_str().unwrap() {
"CONNECT" => {
let message = WS2PConnectMessageV1::parse(m, currency.to_string())
.expect("Failed to parsing CONNECT Message !");
if message.verify() && message.pubkey == self.remote_pubkey.unwrap() {
match self.state {
WS2PConnectionState::WaitingConnectMess => {
trace!("CONNECT sig is valid.");
self.state = WS2PConnectionState::ConnectMessOk;
self.remote_challenge = message.challenge.clone();
let mut response = WS2PAckMessageV1 {
currency: currency.to_string(),
pubkey: key_pair.pubkey,
challenge: self.remote_challenge.clone(),
signature: None,
};
response.signature = Some(response.sign(key_pair));
return WS2PConnectionMessagePayload::ValidConnectMessage(
serde_json::to_string(&response).unwrap(),
self.state.clone(),
);
}
_ => return WS2PConnectionMessagePayload::InvalidMessage,
}
} else {
warn!("The signature of message CONNECT is invalid !")
}
}
"ACK" => {
let mut message = WS2PAckMessageV1::parse(m, currency.to_string())
.expect("Failed to parsing ACK Message !");
message.challenge = self.challenge.to_string();
if message.verify() {
trace!("ACK sig is valid.");
self.state = match self.state {
WS2PConnectionState::ConnectMessOk => {
WS2PConnectionState::AckMessOk
}
WS2PConnectionState::OkMessOkWaitingAckMess => {
WS2PConnectionState::Established
}
_ => return WS2PConnectionMessagePayload::InvalidMessage,
};
let mut response = WS2POkMessageV1 {
currency: currency.to_string(),
pubkey: key_pair.pubkey,
challenge: self.challenge.to_string(),
signature: None,
};
response.signature = Some(response.sign(key_pair));
return WS2PConnectionMessagePayload::ValidAckMessage(
serde_json::to_string(&response).unwrap(),
self.state.clone(),
);
} else {
warn!("The signature of message ACK is invalid !")
}
}
"OK" => {
let mut message = WS2POkMessageV1::parse(m, currency.to_string())
.expect("Failed to parsing OK Message !");
trace!("Received OK");
message.challenge = self.remote_challenge.to_string();
message.pubkey = self.remote_pubkey.expect("fail to get remote pubkey !");
if message.verify() {
trace!("OK sig is valid.");
match self.state {
WS2PConnectionState::ConnectMessOk => {
self.state = WS2PConnectionState::OkMessOkWaitingAckMess;
return WS2PConnectionMessagePayload::ValidOk(
self.state.clone(),
);
}
WS2PConnectionState::AckMessOk => {
info!(
"WS2P Connection established with the key {}",
self.remote_pubkey.expect("fail to get remote pubkey !")
);
self.state = WS2PConnectionState::Established;
return WS2PConnectionMessagePayload::ValidOk(
self.state.clone(),
);
}
_ => {
warn!("WS2P Error : OK message not expected !");
return WS2PConnectionMessagePayload::InvalidMessage;
}
}
} else {
warn!("The signature of message OK is invalid !");
return WS2PConnectionMessagePayload::InvalidMessage;
}
}
&_ => debug!("unknow message"),
};
}
};
if let Some(req_id) = m.get("reqId") {
match req_id.as_str() {
Some(req_id) => match m.get("body") {
Some(body) => {
trace!("WS2P : Receive DAL Request from {}.", self.node_full_id());
match u32::from_str_radix(req_id, 16) {
Ok(req_id) => {
return WS2PConnectionMessagePayload::DalRequest(
ModuleReqId(req_id),
body.clone(),
);
}
Err(_) => return WS2PConnectionMessagePayload::WrongFormatMessage,
}
}
None => {
warn!("WS2P Error : invalid format : Request must contain a field body !");
return WS2PConnectionMessagePayload::WrongFormatMessage;
}
},
None => {
warn!("WS2P Error : invalid format : Request must contain a field body !");
return WS2PConnectionMessagePayload::WrongFormatMessage;
}
}
}
if let Some(req_id) = m.get("resId") {
match req_id.as_str() {
Some(req_id_str) => match m.get("body") {
Some(body) => match u32::from_str_radix(req_id_str, 16) {
Ok(req_id) => {
return WS2PConnectionMessagePayload::ReqResponse(
ModuleReqId(req_id),
body.clone(),
)
}
Err(_) => return WS2PConnectionMessagePayload::WrongFormatMessage,
},
None => match m.get("err") {
Some(err) => warn!("Error in req : {:?}", err),
None => return WS2PConnectionMessagePayload::WrongFormatMessage,
},
},
None => return WS2PConnectionMessagePayload::WrongFormatMessage,
}
}
if let Some(body) = m.get("body") {
match body.get("name") {
Some(s) => if s.is_string() {
match s.as_str().unwrap() {
"BLOCK" => match body.get("block") {
Some(block) => {
if let Some(network_block) = parse_json_block(&block) {
return WS2PConnectionMessagePayload::Document(
NetworkDocument::Block(network_block),
);
} else {
info!("WS2PSignal: receive invalid block (wrong format).");
};
}
None => return WS2PConnectionMessagePayload::WrongFormatMessage,
},
"HEAD" => match body.get("heads") {
Some(heads) => match heads.as_array() {
Some(heads_array) => {
return WS2PConnectionMessagePayload::Heads(heads_array.clone())
}
None => return WS2PConnectionMessagePayload::WrongFormatMessage,
},
None => return WS2PConnectionMessagePayload::WrongFormatMessage,
},
"PEER" => return self.parse_and_check_peer_message(body),
"CERTIFICATION" => {
trace!("WS2P : Receive CERTIFICATION from {}.", self.node_full_id());
/*return WS2PConnectionMessagePayload::Document(
NetworkDocument::Certification(_)
);*/
}
_ => {
/*trace!(
"WS2P : Receive Unknow Message from {}.",
self.node_full_id()
);*/
return WS2PConnectionMessagePayload::UnknowMessage;
}
};
},
None => {
warn!("WS2P Error : invalid format : Body must contain a field name !");
return WS2PConnectionMessagePayload::WrongFormatMessage;
}
}
};
WS2PConnectionMessagePayload::UnknowMessage
}
pub fn parse_and_check_peer_message(
&mut self,
body: &serde_json::Value,
) -> WS2PConnectionMessagePayload {
match body.get("peer") {
Some(peer) => match peer.get("pubkey") {
Some(raw_pubkey) => match PublicKey::from_base58(raw_pubkey.as_str().unwrap_or(""))
{
Ok(pubkey) => {
let mut ws2p_endpoints: Vec<NetworkEndpoint> = Vec::new();
match peer.get("endpoints") {
Some(endpoints) => match endpoints.as_array() {
Some(array_endpoints) => {
for endpoint in array_endpoints {
if let Some(ep) = NetworkEndpoint::parse_from_raw(
endpoint.as_str().unwrap_or(""),
pubkey,
0,
0,
) {
if ep.api() == NetworkEndpointApi(String::from("WS2P"))
{
ws2p_endpoints.push(ep);
}
}
}
WS2PConnectionMessagePayload::PeerCard(
body.clone(),
ws2p_endpoints,
)
}
None => WS2PConnectionMessagePayload::WrongFormatMessage,
},
None => WS2PConnectionMessagePayload::WrongFormatMessage,
}
}
Err(_) => WS2PConnectionMessagePayload::WrongFormatMessage,
},
None => WS2PConnectionMessagePayload::WrongFormatMessage,
},
None => WS2PConnectionMessagePayload::WrongFormatMessage,
}
}
}
extern crate duniter_crypto;
extern crate duniter_documents;
extern crate duniter_message;
extern crate duniter_module;
extern crate duniter_network;
extern crate serde_json;
extern crate sqlite;
extern crate websocket;
use duniter_crypto::keys::{ed25519, PublicKey};
use duniter_network::network_endpoint::{NetworkEndpoint, NetworkEndpointApi};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EndpointApi {
WS2P,
//WS2PS,
//WS2PTOR,
//DASA,
//BMA,
//BMAS,
}
impl From<u32> for EndpointApi {
fn from(integer: u32) -> Self {
match integer {
_ => EndpointApi::WS2P,
}
}
}
pub fn string_to_api(api: &str) -> Option<EndpointApi> {
match api {
"WS2P" => Some(EndpointApi::WS2P),
//"WS2PS" => Some(EndpointApi::WS2PS),
//"WS2PTOR" => Some(EndpointApi::WS2PTOR),
//"DASA" => Some(EndpointApi::DASA),
//"BASIC_MERKLED_API" => Some(EndpointApi::BMA),
//"BMAS" => Some(EndpointApi::BMAS),
&_ => None,
}
}
pub fn api_to_integer(api: &NetworkEndpointApi) -> i64 {
match api.0.as_str() {
"WS2P" => 1,
//EndpointApi::WS2PS => 2,
//EndpointApi::WS2PTOR => 3,
//EndpointApi::DASA => 4,
//EndpointApi::BMA => 5,
//EndpointApi::BMAS => 6,
_ => 0,
}
}
pub fn get_endpoints_for_api(
db: &sqlite::Connection,
api: NetworkEndpointApi,
) -> Vec<NetworkEndpoint> {
let mut cursor:sqlite::Cursor = db
.prepare("SELECT hash_full_id, status, node_id, pubkey, api, version, endpoint, last_check FROM endpoints WHERE api=? ORDER BY status DESC;")
.expect("get_endpoints_for_api() : Error in SQL request !")
.cursor();
cursor
.bind(&[sqlite::Value::Integer(api_to_integer(&api))])
.expect("get_endpoints_for_api() : Error in cursor binding !");
let mut endpoints = Vec::new();
while let Some(row) = cursor
.next()
.expect("get_endpoints_for_api() : Error in cursor.next()")
{
let raw_ep = row[6].as_string().unwrap().to_string();
let ep_issuer = ed25519::PublicKey::from_base58(row[3].as_string().unwrap()).unwrap();
let mut ep = match NetworkEndpoint::parse_from_raw(
&raw_ep,
ep_issuer,
row[1].as_integer().unwrap() as u32,
row[7].as_integer().unwrap() as u64,
) {
Some(ep) => ep,
None => panic!(format!("Fail to parse endpoint : {}", raw_ep)),
};
ep.set_status(row[1].as_integer().unwrap() as u32);
ep.set_last_check(row[7].as_integer().unwrap() as u64);
endpoints.push(ep);
}
endpoints
}
pub fn write_endpoint(
db: &sqlite::Connection,
endpoint: &NetworkEndpoint,
new_status: u32,
new_last_check: u64,
) {
let hash_full_id = endpoint
.node_full_id()
.expect("Fail to write endpoint : node_full_id() return None !")
.sha256();
// Check if endpoint it's already written
let mut cursor: sqlite::Cursor = db
.prepare("SELECT status FROM endpoints WHERE hash_full_id=? ORDER BY status DESC;")
.expect("write_endpoint() : Error in SQL request !")
.cursor();
cursor
.bind(&[sqlite::Value::String(hash_full_id.to_string())])
.expect("write_endpoint() : Error in cursor binding !");
// If endpoint it's already written, update status
if let Some(row) = cursor
.next()
.expect("write_endpoint() : Error in cursor.next()")
{
if row[0].as_integer().expect("fail to read ep status !") as u32 != endpoint.status() {
db.execute(format!(
"UPDATE endpoints SET status={} WHERE hash_full_id='{}'",
endpoint.status(),
hash_full_id
)).expect("Fail to parse SQL request update endpoint status !");
}
} else {
if let &NetworkEndpoint::V1(ref ep_v1) = endpoint {
db
.execute(
format!(
"INSERT INTO endpoints (hash_full_id, status, node_id, pubkey, api, version, endpoint, last_check) VALUES ('{}', {}, {}, '{}', {}, {}, '{}', {});",
ep_v1.hash_full_id.expect("ep_v1.hash_full_id = None"), new_status, ep_v1.node_id.expect("ep_v1.node_id = None").0,
ep_v1.issuer.to_string(), api_to_integer(&ep_v1.api),
ep_v1.version, ep_v1.raw_endpoint, new_last_check
)
)
.expect("Fail to parse SQL request INSERT endpoint !");
} else {
panic!("write_endpoint() : Endpoint version is not supported !")
}
}
}
extern crate duniter_crypto;
extern crate duniter_network;
extern crate serde;
extern crate serde_json;
use duniter_network::NetworkRequest;
pub fn network_request_to_json(request: &NetworkRequest) -> serde_json::Value {
let (request_id, request_type, request_params) = match *request {
NetworkRequest::GetCurrent(ref req_full_id, _receiver) => {
(req_full_id.1, "CURRENT", json!({}))
}
NetworkRequest::GetBlocks(ref req_full_id, _receiver, count, from_mumber) => (
req_full_id.1,
"BLOCKS_CHUNK",
json!({
"count": count,
"fromNumber": from_mumber
}),
),
NetworkRequest::GetRequirementsPending(ref req_full_id, _receiver, min_cert) => (
req_full_id.1,
"WOT_REQUIREMENTS_OF_PENDING",
json!({ "minCert": min_cert }),
),
NetworkRequest::GetConsensus(_) => {
panic!("GetConsensus() request must be not convert to json !");
}
NetworkRequest::GetHeadsCache(_) => {
panic!("GetHeadsCache() request must be not convert to json !");
}
NetworkRequest::GetEndpoints(_) => {
panic!("GetEndpoints() request must be not convert to json !");
}
};
json!({
"reqId": request_id,
"body" : {
"name": request_type,
"params": request_params
}
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment