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

Merge branch...

Merge branch '68-add-crates-blockchain-conf-core-dal-message-module-network-tui-and-ws2p' into 'dev'

Resolve "Add crates blockchain, conf, core, dal, message, module, network, tui and ws2p"

Closes #68

See merge request !58
parents ddf76f37 d976f9bc
Branches
Tags
1 merge request!58Resolve "Add crates blockchain, conf, core, dal, message, module, network, tui and ws2p"
stages: stages:
- build_and_tests - build_and_tests
- clippy
- fmt - fmt
- clippy
- publish - publish
before_script: before_script:
...@@ -12,7 +12,8 @@ build_and_tests:stable: ...@@ -12,7 +12,8 @@ build_and_tests:stable:
tags: tags:
- redshift-rs-stable - redshift-rs-stable
script: script:
- cargo test --all-features - cargo build --features strict
- cargo test --all
build_and_tests:beta: build_and_tests:beta:
stage: build_and_tests stage: build_and_tests
...@@ -20,7 +21,8 @@ build_and_tests:beta: ...@@ -20,7 +21,8 @@ build_and_tests:beta:
- redshift-rs-beta - redshift-rs-beta
script: script:
- rustup update - rustup update
- cargo test --all-features - cargo build --features strict
- cargo test --all
when: manual when: manual
allow_failure: true allow_failure: true
...@@ -30,32 +32,33 @@ build_and_tests:nightly: ...@@ -30,32 +32,33 @@ build_and_tests:nightly:
tags: tags:
- redshift-rs-nightly - redshift-rs-nightly
script: script:
- cargo test --all-features - cargo build --features strict
- cargo test --all
when: manual when: manual
allow_failure: true allow_failure: true
clippy: fmt:
stage: clippy stage: fmt
image: rustlang/rust:nightly image: rustlang/rust:nightly
tags: tags:
- redshift-rs-nightly - redshift-rs-nightly
before_script: before_script:
- export PATH="$HOME/.cargo/bin:$PATH" - export PATH="$HOME/.cargo/bin:$PATH"
- cargo install --force clippy --verbose - cargo install --force rustfmt-nightly
script: script:
- cargo clippy --all -- -D warnings --verbose - cargo fmt -- --check
allow_failure: true allow_failure: true
fmt: clippy:
stage: fmt stage: clippy
image: rustlang/rust:nightly image: rustlang/rust:nightly
tags: tags:
- redshift-rs-nightly - redshift-rs-nightly
before_script: before_script:
- export PATH="$HOME/.cargo/bin:$PATH" - export PATH="$HOME/.cargo/bin:$PATH"
- cargo install --force rustfmt-nightly - cargo install --force clippy --verbose
script: script:
- cargo fmt -- --write-mode=diff - cargo clippy --all -- -D warnings --verbose
allow_failure: true allow_failure: true
publish:crate: publish:crate:
...@@ -71,3 +74,20 @@ publish:crate: ...@@ -71,3 +74,20 @@ publish:crate:
- tags - tags
allow_failure: false allow_failure: false
when: manual when: manual
pages:
stage: publish
tags:
- redshift-rs-stable
before_script:
- export PATH="$HOME/.cargo/bin:$PATH"
script:
- cargo doc
- mv target/doc public
- ls public
artifacts:
untracked: true
paths:
- public
allow_failure: true
when: manual
\ No newline at end of file
This diff is collapsed.
[package]
name = "durs"
version = "0.1.0"
authors = ["librelois <elois@duniter.org>","nanocryk <nanocryk@duniter.org>"]
description = "DUniter-RS (durs) is a new implementation of Duniter protocol and software in Rust, a safe, concurrent, practical language"
license = "AGPL-3.0"
[dependencies]
clap = "2.31.2"
duniter-core = { path = "./core" }
duniter-tui = { path = "./tui" }
duniter-ws2p = { path = "./ws2p" }
lazy_static = "1.0.0"
serde_json = "1.0.9"
websocket = "0.20.2"
[features]
# Treat warnings as a build error.
strict = []
[workspace] [workspace]
members = [ members = [
"wotb", "blockchain",
"conf",
"core",
"crypto", "crypto",
"dal",
"documents", "documents",
"message",
"module",
"network",
"tui",
"wotb",
"ws2p",
] ]
[package]
name = "duniter-blockchain"
version = "0.1.0"
authors = ["librelois <elois@ifee.fr>"]
description = "Blockchain module 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" }
log = "0.4.1"
pbr = "1.0.0"
rand = "0.4.2"
serde = "1.0.24"
serde_derive = "1.0.24"
serde_json = "1.0.9"
sqlite = "0.23.9"
\ No newline at end of file
This diff is collapsed.
// Copyright (C) 2018 The Duniter Project Developers.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
extern crate duniter_crypto;
extern crate duniter_dal;
extern crate duniter_documents;
extern crate duniter_wotb;
use duniter_crypto::keys::ed25519;
use duniter_dal::block::{DALBlock, WotEvent};
use duniter_dal::writers::requests::DBWriteRequest;
use duniter_documents::blockchain::v10::documents::BlockDocument;
use duniter_documents::blockchain::Document;
use duniter_wotb::{NodeId, WebOfTrust};
use std::collections::HashMap;
pub fn try_stack_up_completed_block<W: WebOfTrust + Sync>(
block: &BlockDocument,
wotb_index: &HashMap<ed25519::PublicKey, NodeId>,
wot: &W,
) -> (bool, Vec<DBWriteRequest>, Vec<WotEvent>) {
debug!(
"BlockchainModule : try stack up complete block {}",
block.blockstamp()
);
let mut db_requests = Vec::new();
let mut wot_events = Vec::new();
let mut wot_copy: W = wot.clone();
let mut wotb_index_copy: HashMap<ed25519::PublicKey, NodeId> = wotb_index.clone();
let current_blockstamp = block.blockstamp();
let mut identities = HashMap::with_capacity(block.identities.len());
for identity in block.identities.clone() {
identities.insert(identity.issuers()[0], identity);
}
for joiner in block.joiners.clone() {
let pubkey = joiner.clone().issuers()[0];
if let Some(idty_doc) = identities.get(&pubkey) {
// Newcomer
let wotb_id = NodeId(wot_copy.size());
wot_events.push(WotEvent::AddNode(pubkey, wotb_id));
wot_copy.add_node();
wotb_index_copy.insert(pubkey, wotb_id);
db_requests.push(DBWriteRequest::CreateIdentity(
wotb_id,
current_blockstamp.clone(),
block.median_time,
idty_doc.clone(),
));
} else {
// Renewer
let wotb_id = wotb_index_copy[&joiner.issuers()[0]];
wot_events.push(WotEvent::EnableNode(wotb_id));
wot_copy.set_enabled(wotb_id, true);
db_requests.push(DBWriteRequest::RenewalIdentity(
joiner.issuers()[0],
block.blockstamp(),
block.median_time,
));
}
}
for active in block.actives.clone() {
let pubkey = active.issuers()[0];
if !identities.contains_key(&pubkey) {
let wotb_id = wotb_index_copy[&pubkey];
wot_events.push(WotEvent::EnableNode(wotb_id));
wot_copy.set_enabled(wotb_id, true);
db_requests.push(DBWriteRequest::RenewalIdentity(
pubkey,
block.blockstamp(),
block.median_time,
));
}
}
for exclusion in block.excluded.clone() {
let wotb_id = wotb_index_copy[&exclusion];
wot_events.push(WotEvent::DisableNode(wotb_id));
wot_copy.set_enabled(wotb_id, false);
db_requests.push(DBWriteRequest::ExcludeIdentity(
wotb_id,
block.blockstamp(),
block.median_time,
));
}
for revocation in block.revoked.clone() {
let compact_revoc = revocation.to_compact_document();
let wotb_id = wotb_index_copy[&compact_revoc.issuer];
wot_events.push(WotEvent::DisableNode(wotb_id));
wot_copy.set_enabled(wotb_id, false);
db_requests.push(DBWriteRequest::RevokeIdentity(
wotb_id,
block.blockstamp(),
block.median_time,
));
}
for certification in block.certifications.clone() {
trace!("try_stack_up_completed_block: apply cert...");
let compact_cert = certification.to_compact_document();
let wotb_node_from = wotb_index_copy[&compact_cert.issuer];
let wotb_node_to = wotb_index_copy[&compact_cert.target];
wot_events.push(WotEvent::AddLink(wotb_node_from, wotb_node_to));
wot_copy.add_link(wotb_node_from, wotb_node_to);
db_requests.push(DBWriteRequest::CreateCert(
block.blockstamp(),
block.median_time,
compact_cert,
));
trace!("try_stack_up_completed_block: apply cert...success.");
}
/*// Calculate the state of the wot
if !wot_events.is_empty() && verif_level != SyncVerificationLevel::FastSync() {
// Calculate sentries_count
let sentries_count = wot_copy.get_sentries(3).len();
// Calculate average_density
let average_density = calculate_average_density::<W>(&wot_copy);
let sentry_requirement =
get_sentry_requirement(block.members_count, G1_PARAMS.step_max);
// Calculate distances and connectivities
let (average_distance, distances, average_connectivity, connectivities) =
compute_distances::<W>(
&wot_copy,
sentry_requirement,
G1_PARAMS.step_max,
G1_PARAMS.x_percent,
);
// Calculate centralities and average_centrality
let centralities =
calculate_distance_stress_centralities::<W>(&wot_copy, G1_PARAMS.step_max);
let average_centrality =
(centralities.iter().sum::<u64>() as f64 / centralities.len() as f64) as usize;
// Register the state of the wot
duniter_dal::register_wot_state(
db,
&WotState {
block_number: block.number.0,
block_hash: block.hash.unwrap().to_string(),
sentries_count,
average_density,
average_distance,
distances,
average_connectivity,
connectivities: connectivities
.iter()
.map(|c| {
if *c > *G1_CONNECTIVITY_MAX {
*G1_CONNECTIVITY_MAX
} else {
*c
}
})
.collect(),
average_centrality,
centralities,
},
);
}*/
// Write block in bdd
db_requests.push(DBWriteRequest::WriteBlock(DALBlock {
block: block.clone(),
fork: 0,
isolate: false,
}));
(true, db_requests, wot_events)
}
This diff is collapsed.
[package]
name = "duniter-conf"
version = "0.1.0"
authors = ["librelois <elois@ifee.fr>"]
description = "Configuration module for the Duniter project."
license = "AGPL-3.0"
[lib]
path = "lib.rs"
[dependencies]
rand = "0.4.2"
serde = "1.0.24"
serde_derive = "1.0.24"
serde_json = "1.0.9"
duniter-crypto = { path = "../crypto" }
duniter-module = { path = "../module" }
[features]
# Treat warnings as a build error.
strict = []
\ No newline at end of file
// Copyright (C) 2018 The Duniter Project Developers.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Defined the few global types used by all modules,
//! as well as the DuniterModule trait that all modules must implement.
#![cfg_attr(feature = "strict", deny(warnings))]
#![deny(
missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
unused_qualifications
)]
#[macro_use]
extern crate serde_json;
extern crate duniter_crypto;
extern crate duniter_module;
extern crate rand;
extern crate serde;
use duniter_crypto::keys::{ed25519, KeyPair, PrivateKey, PublicKey};
use duniter_module::{Currency, DuniterConf, DuniterConfV1, RequiredKeys, RequiredKeysContent};
use rand::Rng;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::env;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
static USER_DATAS_FOLDER: &'static str = "durs-dev";
/// If no currency is specified by the user, is the currency will be chosen by default
pub static DEFAULT_CURRRENCY: &'static str = "g1";
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// Keypairs filled in by the user (via a file or by direct entry in the terminal).
pub struct DuniterKeyPairs {
/// Keypair used by the node to sign its communications with other nodes. This keypair is mandatory, if it's not filled in, a random keypair is generated.
pub network_keypair: ed25519::KeyPair,
/// Keypair used to sign the blocks forged by this node. If this keypair is'nt filled in, the node will not calculate blocks.
pub member_keypair: Option<ed25519::KeyPair>,
}
impl Serialize for DuniterKeyPairs {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let member_sec = if let Some(member_keypair) = self.member_keypair {
member_keypair.private_key().to_string()
} else {
String::from("")
};
let member_pub = if let Some(member_keypair) = self.member_keypair {
member_keypair.pubkey.to_string()
} else {
String::from("")
};
let mut state = serializer.serialize_struct("DuniterKeyPairs", 4)?;
state.serialize_field(
"network_sec",
&self.network_keypair.private_key().to_string().as_str(),
)?;
state.serialize_field(
"network_pub",
&self.network_keypair.pubkey.to_string().as_str(),
)?;
state.serialize_field("member_sec", member_sec.as_str())?;
state.serialize_field("member_pub", member_pub.as_str())?;
state.end()
}
}
impl DuniterKeyPairs {
/// Returns only the keys indicated as required
pub fn get_required_keys_content(
required_keys: RequiredKeys,
keypairs: DuniterKeyPairs,
) -> RequiredKeysContent<ed25519::KeyPair> {
match required_keys {
RequiredKeys::MemberKeyPair() => {
RequiredKeysContent::MemberKeyPair(keypairs.member_keypair)
}
RequiredKeys::MemberPublicKey() => {
RequiredKeysContent::MemberPublicKey(if let Some(keys) = keypairs.member_keypair {
Some(keys.pubkey)
} else {
None
})
}
RequiredKeys::NetworkKeyPair() => {
RequiredKeysContent::NetworkKeyPair(keypairs.network_keypair)
}
RequiredKeys::NetworkPublicKey() => {
RequiredKeysContent::NetworkPublicKey(keypairs.network_keypair.pubkey)
}
RequiredKeys::None() => RequiredKeysContent::None(),
}
}
}
fn _use_json_macro() -> serde_json::Value {
json!({})
}
fn generate_random_keypair() -> ed25519::KeyPair {
let mut rng = rand::thread_rng();
let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();
generator.generate(&[rng.gen::<u8>(); 8], &[rng.gen::<u8>(); 8])
}
fn generate_random_node_id() -> u32 {
let mut rng = rand::thread_rng();
rng.gen::<u32>()
}
/// Return the user datas folder name
pub fn get_user_datas_folder() -> &'static str {
USER_DATAS_FOLDER
}
/// Returns the path to the folder containing the user data of the running profile
pub fn datas_path(profile: &str, currency: &Currency) -> PathBuf {
let mut datas_path = match env::home_dir() {
Some(path) => path,
None => panic!("Impossible to get your home dir!"),
};
datas_path.push(".config/");
datas_path.push(USER_DATAS_FOLDER);
datas_path.push(profile);
datas_path.push(currency.to_string());
datas_path
}
/// Load configuration.
pub fn load_conf(profile: &str) -> (DuniterConf, DuniterKeyPairs) {
// Define and create datas directory if not exist
let mut profile_path = match env::home_dir() {
Some(path) => path,
None => panic!("Impossible to get your home dir !"),
};
profile_path.push(".config/");
if !profile_path.as_path().exists() {
fs::create_dir(profile_path.as_path()).expect("Impossible to create ~/.config dir !");
}
profile_path.push(USER_DATAS_FOLDER);
if !profile_path.as_path().exists() {
fs::create_dir(profile_path.as_path()).expect(&format!(
"Impossible to create ~/.config/{} dir !",
USER_DATAS_FOLDER
));
}
profile_path.push(profile);
if !profile_path.as_path().exists() {
fs::create_dir(profile_path.as_path()).expect("Impossible to create your profile dir !");
}
// Load conf
let (conf, keypairs) = load_conf_at_path(profile, &profile_path);
// Create currency dir
profile_path.push(conf.currency().to_string());
if !profile_path.as_path().exists() {
fs::create_dir(profile_path.as_path()).expect("Impossible to create currency dir !");
}
// Return conf and keypairs
(conf, keypairs)
}
/// Load configuration. at specified path
pub fn load_conf_at_path(profile: &str, profile_path: &PathBuf) -> (DuniterConf, DuniterKeyPairs) {
// Default conf
let mut conf = DuniterConfV1 {
profile: String::from(profile),
currency: Currency::Str(DEFAULT_CURRRENCY.to_string()),
my_node_id: generate_random_node_id(),
modules: serde_json::Value::Null,
};
// Get KeyPairs
let mut keypairs_path = profile_path.clone();
keypairs_path.push("keypairs.json");
let keypairs = if keypairs_path.as_path().exists() {
if let Ok(mut f) = File::open(keypairs_path.as_path()) {
let mut contents = String::new();
if f.read_to_string(&mut contents).is_ok() {
let json_conf: serde_json::Value =
serde_json::from_str(&contents).expect("Conf: Fail to parse keypairs file !");
if let Some(network_sec) = json_conf.get("network_sec") {
if let Some(network_pub) = json_conf.get("network_pub") {
let network_sec = network_sec
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let network_pub = network_pub
.as_str()
.expect("Conf: Fail to parse keypairs file !");
DuniterKeyPairs {
network_keypair: ed25519::KeyPair {
privkey: PrivateKey::from_base58(network_sec)
.expect("conf : keypairs file : fail to parse network_sec !"),
pubkey: PublicKey::from_base58(network_pub)
.expect("conf : keypairs file : fail to parse network_pub !"),
},
member_keypair: None,
}
} else {
panic!("Fatal error : keypairs file wrong format : no field salt !")
}
} else {
panic!("Fatal error : keypairs file wrong format : no field password !")
}
} else {
panic!("Fail to read keypairs file !");
}
} else {
panic!("Fail to open keypairs file !");
}
} else {
// Create keypairs file with random keypair
let keypairs = DuniterKeyPairs {
network_keypair: generate_random_keypair(),
member_keypair: None,
};
write_keypairs_file(&keypairs_path, &keypairs)
.expect("Fatal error : fail to write default keypairs file !");
keypairs
};
// Open conf file
let mut conf_path = profile_path.clone();
conf_path.push("conf.json");
if conf_path.as_path().exists() {
if let Ok(mut f) = File::open(conf_path.as_path()) {
let mut contents = String::new();
if f.read_to_string(&mut contents).is_ok() {
let json_conf: serde_json::Value =
serde_json::from_str(&contents).expect("Conf: Fail to parse conf file !");
if let Some(currency) = json_conf.get("currency") {
conf.currency = Currency::Str(
currency
.as_str()
.expect("Conf: fail to parse currency field !")
.to_string(),
);
};
if let Some(node_id) = json_conf.get("node_id") {
conf.my_node_id =
u32::from_str_radix(
node_id
.as_str()
.expect("Conf : fail to parse node_id field !"),
16,
).expect("Fail to load conf: node_id must be an hexadecimal number !");
};
if let Some(modules_conf) = json_conf.get("modules") {
conf.modules = modules_conf.clone();
};
}
} else {
panic!("Fail to open conf file !");
}
} else {
// Create conf file with default conf
write_conf_file(&conf_path, &DuniterConf::V1(conf.clone()))
.expect("Fatal error : fail to write default conf file !");
}
// Return conf and keypairs
(DuniterConf::V1(conf), keypairs)
}
/// Save keypairs in profile folder
pub fn write_keypairs_file(
file_path: &PathBuf,
keypairs: &DuniterKeyPairs,
) -> Result<(), std::io::Error> {
let mut f = try!(File::create(file_path.as_path()));
try!(
f.write_all(
serde_json::to_string_pretty(keypairs)
.expect("Fatal error : fail to write default keypairs file !")
.as_bytes()
)
);
try!(f.sync_all());
Ok(())
}
/// Save configuration in profile folder
pub fn write_conf_file(file_path: &PathBuf, conf: &DuniterConf) -> Result<(), std::io::Error> {
match *conf {
DuniterConf::V1(ref conf_v1) => {
let mut f = try!(File::create(file_path.as_path()));
try!(
f.write_all(
serde_json::to_string_pretty(conf_v1)
.expect("Fatal error : fail to write default conf file !")
.as_bytes()
)
);
try!(f.sync_all());
}
_ => {
panic!("Fatal error : Conf version is not supported !");
}
}
Ok(())
}
/// Returns the path to the database containing the blockchain
pub fn get_db_path(profile: &str, currency: &Currency, sync: bool) -> PathBuf {
if sync {
let mut db_path = PathBuf::new();
let mut db_name = String::from(profile);
db_name.push_str("_durs.db");
db_path.push("/dev/shm");
db_path.push(db_name);
db_path
} else {
let mut db_path = datas_path(profile, &currency);
db_path.push("blockchain.db");
db_path
}
}
/// Returns the path to the binary file containing the state of the web of trust
pub fn get_wot_path(profile: String, currency: &Currency) -> PathBuf {
let mut wot_path = match env::home_dir() {
Some(path) => path,
None => panic!("Impossible to get your home dir!"),
};
wot_path.push(".config/");
wot_path.push(USER_DATAS_FOLDER);
wot_path.push(profile);
wot_path.push(currency.to_string());
wot_path.push("wot.bin");
wot_path
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_conf_file() {
let (conf, _keys) = load_conf_at_path("test", &PathBuf::from("./test/"));
assert_eq!(
conf.modules()
.get("ws2p")
.expect("Not found ws2p conf")
.clone(),
json!({
"sync_peers": [{
"pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
"ws2p_endpoints": ["WS2P c1c39a0a i3.ifee.fr 80 /ws2p"]
},{
"pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG",
"ws2p_endpoints": ["WS2P 15af24db g1.ifee.fr 80 /ws2p"]
},{
"pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef",
"ws2p_endpoints": ["WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p"]
}]
})
);
}
}
{
"currency": "g1",
"node_id": "357fb4b",
"modules": {
"tui": null,
"ws2p": {
"sync_peers": [{
"pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
"ws2p_endpoints": ["WS2P c1c39a0a i3.ifee.fr 80 /ws2p"]
},{
"pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG",
"ws2p_endpoints": ["WS2P 15af24db g1.ifee.fr 80 /ws2p"]
},{
"pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef",
"ws2p_endpoints": ["WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p"]
}]
}
}
}
{
"network_sec": "RcrnPyfgEaet2VqQvLeBsgKvfaoUqinQnCooP9bFoDr1W1Focos351r4xBee6Hj2RaV6RRV8PxpdMR6hVVhNNDb",
"network_pub": "39SvsGzGZjdCh1wFS1QKdCR1ZB6rTxRbTZRW8sY5fhq7",
"member_sec": "",
"member_pub": ""
}
\ No newline at end of file
[package]
name = "duniter-core"
version = "0.1.0"
authors = ["librelois <elois@ifee.fr>"]
description = "Duniter-rs core."
license = "AGPL-3.0"
[lib]
path = "lib.rs"
[dependencies]
clap = {version = "2.31.2", features = ["yaml"]}
duniter-blockchain = { path = "../blockchain" }
duniter-conf = { path = "../conf" }
duniter-crypto = { path = "../crypto" }
duniter-message = { path = "../message" }
duniter-module = { path = "../module" }
lazy_static = "1.0.0"
log = "0.4.1"
rand = "0.4.2"
regex = "0.2.6"
rust-crypto = "0.2.36"
serde = "1.0.24"
serde_derive = "1.0.24"
serde_json = "1.0.9"
simplelog = "0.5.1"
sqlite = "0.23.9"
threadpool = "1.7.1"
[features]
# Treat warnings as a build error.
strict = []
\ No newline at end of file
name: durs
version: "0.1.0"
author: Elois L. <elois@duniter.org>
about: Rust implementation of Duniter
args:
- profile:
short: p
long: profile
value_name: CUSTOM_PROFILE
help: Set a custom datas folder
takes_value: true
- logs:
short: l
long : logs
value_name: LOGS_LEVEL
takes_value: true
possible_values: ["e", "w", "i", "d", "t", "error", "warn", "info", "debug", "trace"]
help: Set the level of logs verbosity
long_help: "Set the level of logs verbosity :\n
error : print serious errors\n
warn : print hazardous situations\n
info : default level\n
debug : print a lot of debug informations\n
trace : print all traces (highly verbose)"
subcommands:
- start:
about: start duniter server
version: "0.1.0"
author: Elois L. <elois@duniter.org>
- sync_ts:
about: synchronization via a duniter-ts database
version: "0.1.0"
author: Elois L. <elois@duniter.org>
args:
- TS_PROFILE:
help: Set the ts profile to use
index: 1
- cautious:
short: c
long: cautious
help: cautious mode (check all protocol rules, very slow)
- msync_ts:
about: synchronization in memory mode via a duniter-ts database
version: "0.1.0"
author: Elois L. <elois@duniter.org>
args:
- TS_PROFILE:
help: Set the ts profile to use
index: 1
- cautious:
short: c
long: cautious
help: cautious mode (check all protocol rules, very slow)
- reset:
about: reset data or conf or all
version: "0.1.0"
author: Elois L. <elois@duniter.org>
args:
- DATAS_TYPE:
help : choose type datas to reset
index: 1
possible_values: ["data","conf","all"]
required: true
\ No newline at end of file
// Copyright (C) 2018 The Duniter Project Developers.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Crate containing Duniter-rust core.
#![cfg_attr(feature = "strict", deny(warnings))]
#![deny(
missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
unused_qualifications
)]
#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
extern crate duniter_blockchain;
extern crate duniter_conf;
extern crate duniter_crypto;
extern crate duniter_message;
extern crate duniter_module;
extern crate serde_json;
extern crate simplelog;
extern crate sqlite;
extern crate threadpool;
use self::threadpool::ThreadPool;
use clap::{App, ArgMatches};
use duniter_blockchain::BlockchainModule;
use duniter_conf::DuniterKeyPairs;
use duniter_crypto::keys::ed25519;
use duniter_message::DuniterMessage;
use duniter_module::*;
use log::Level;
use simplelog::*;
use std::env;
use std::fs;
use std::fs::{File, OpenOptions};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
#[derive(Debug)]
/// Duniter Core Datas
pub struct DuniterCore {
/// Does the entered command require to launch server ?
pub start: bool,
/// Software name
pub soft_name: &'static str,
/// Soft version
pub soft_version: &'static str,
/// Keypairs
pub keypairs: DuniterKeyPairs,
/// Duniter configuration
pub conf: DuniterConf,
/// Run duration. Zero = infinite duration.
pub run_duration_in_secs: u64,
/// Sender channel of rooter thread
pub rooter_sender: mpsc::Sender<RooterThreadMessage<DuniterMessage>>,
/// Count the number of plugged modules
pub modules_count: usize,
/// ThreadPool that execute plugged modules
pub thread_pool: ThreadPool,
}
impl DuniterCore {
/// Instantiate Duniter classic node
pub fn new(soft_name: &'static str, soft_version: &'static str) -> Option<DuniterCore> {
DuniterCore::new_specialized_node(soft_name, soft_version, 0, vec![], vec![], None)
}
/// Instantiate Duniter specialize node
pub fn new_specialized_node<'a, 'b>(
soft_name: &'static str,
soft_version: &'static str,
run_duration_in_secs: u64,
external_followers: Vec<mpsc::Sender<DuniterMessage>>,
sup_apps: Vec<App<'a, 'b>>,
sup_apps_fn: Option<&Fn(&str, &ArgMatches) -> ()>,
) -> Option<DuniterCore> {
// Get cli conf
let yaml = load_yaml!("./cli/en.yml");
let cli_conf = App::from_yaml(yaml);
// Math command line arguments
let cli_args = if !sup_apps.is_empty() {
cli_conf.subcommands(sup_apps).get_matches()
} else {
cli_conf.get_matches()
};
// Get datas profile name
let profile = match_profile(&cli_args);
// Init logger
init_logger(profile.as_str(), soft_name, &cli_args);
// Load global conf
let (conf, keypairs) = duniter_conf::load_conf(profile.as_str());
info!("Success to load global conf.");
if let Some(_matches) = cli_args.subcommand_matches("start") {
Some(start(
soft_name,
soft_version,
keypairs,
conf,
run_duration_in_secs,
external_followers,
))
} else if let Some(matches) = cli_args.subcommand_matches("sync_ts") {
let ts_profile = matches.value_of("TS_PROFILE").unwrap_or("duniter_default");
sync_ts(conf, ts_profile, matches.is_present("cautious"));
None
} else if let Some(matches) = cli_args.subcommand_matches("reset") {
let mut profile_path = match env::home_dir() {
Some(path) => path,
None => panic!("Impossible to get your home dir !"),
};
profile_path.push(".config");
profile_path.push(duniter_conf::get_user_datas_folder());
profile_path.push(profile.clone());
if !profile_path.as_path().exists() {
panic!(format!("Error : {} profile don't exist !", profile));
}
match matches.value_of("DATAS_TYPE").unwrap() {
"data" => {
let mut currency_datas_path = profile_path.clone();
currency_datas_path.push("g1");
fs::remove_dir_all(currency_datas_path.as_path())
.expect("Fail to remove all currency datas !");
}
"conf" => {
let mut conf_file_path = profile_path.clone();
conf_file_path.push("conf.json");
fs::remove_file(conf_file_path.as_path()).expect("Fail to remove conf file !");
let mut conf_keys_path = profile_path.clone();
conf_keys_path.push("keypairs.json");
fs::remove_file(conf_keys_path.as_path())
.expect("Fail to remove keypairs file !");
}
"all" => {
fs::remove_dir_all(profile_path.as_path())
.expect("Fail to remove all profile datas !");
}
_ => {}
}
None
} else if let Some(sup_apps_fn) = sup_apps_fn {
sup_apps_fn(profile.as_str(), &cli_args);
None
} else {
panic!("unknow sub-command !")
}
}
/// Start blockchain module
pub fn start_blockchain(&self) {
if self.start {
thread::sleep(Duration::from_secs(2));
// Create blockchain module channel
let (blockchain_sender, blockchain_receiver): (
mpsc::Sender<DuniterMessage>,
mpsc::Receiver<DuniterMessage>,
) = mpsc::channel();
// Send blockchain sender to rooter thread
self.rooter_sender
.send(RooterThreadMessage::ModuleSender(blockchain_sender))
.expect("Fatal error: fail to send blockchain sender to rooter thread !");
// Send modules_count to rooter thread
self.rooter_sender
.send(RooterThreadMessage::ModulesCount(self.modules_count + 1))
.expect("Fatal error: fail to send modules count to rooter thread !");
// Instantiate blockchain module and load is conf
let mut blockchain_module = BlockchainModule::load_blockchain_conf(
&self.conf,
RequiredKeysContent::MemberKeyPair(None),
false,
);
info!("Success to load Blockchain module.");
// Start blockchain module in main thread
blockchain_module.start_blockchain(blockchain_receiver);
}
}
/// Plug a module
pub fn plug<M: DuniterModule<ed25519::KeyPair, DuniterMessage>>(&mut self) {
if self.start {
// Start module in a new thread
let soft_name_clone = self.soft_name.clone();
let soft_version_clone = self.soft_version.clone();
let required_keys = DuniterKeyPairs::get_required_keys_content(
M::ask_required_keys(),
self.keypairs.clone(),
);
let module_conf = if let Some(module_conf_) = self
.conf
.clone()
.modules()
.get(&M::id().to_string().as_str())
{
module_conf_.clone()
} else {
M::default_conf()
};
let rooter_sender_clone = self.rooter_sender.clone();
let conf_clone = self.conf.clone();
self.thread_pool.execute(move || {
M::start(
soft_name_clone,
soft_version_clone,
required_keys,
&conf_clone,
&module_conf,
rooter_sender_clone,
false,
).expect(&format!(
"Fatal error : fail to load {} Module !",
M::id().to_string()
));
});
self.modules_count += 1;
info!("Success to load {} module.", M::id().to_string());
}
}
}
/// Match cli option --profile
pub fn match_profile(cli_args: &ArgMatches) -> String {
String::from(cli_args.value_of("profile").unwrap_or("default"))
}
/// Launch duniter server
pub fn start(
soft_name: &'static str,
soft_version: &'static str,
keypairs: DuniterKeyPairs,
conf: DuniterConf,
run_duration_in_secs: u64,
external_followers: Vec<mpsc::Sender<DuniterMessage>>,
) -> DuniterCore {
info!("Starting Duniter-rs...");
// Create senders channel
let (rooter_sender, main_receiver): (
mpsc::Sender<RooterThreadMessage<DuniterMessage>>,
mpsc::Receiver<RooterThreadMessage<DuniterMessage>>,
) = mpsc::channel();
// Create rooter thread
thread::spawn(move || {
// Wait to receiver modules senders
let mut modules_senders: Vec<mpsc::Sender<DuniterMessage>> = Vec::new();
let mut modules_count_expected = None;
while modules_count_expected.is_none()
|| modules_senders.len() < modules_count_expected.unwrap() + 1
{
match main_receiver.recv_timeout(Duration::from_secs(20)) {
Ok(mess) => {
match mess {
RooterThreadMessage::ModuleSender(module_sender) => {
// Subscribe this module to all others modules
for other_module in modules_senders.clone() {
if other_module
.send(DuniterMessage::Followers(vec![module_sender.clone()]))
.is_err()
{
panic!("Fatal error : fail to send all modules senders to all modules !");
}
}
// Subcribe this module to all external_followers
for external_follower in external_followers.clone() {
if external_follower
.send(DuniterMessage::Followers(vec![module_sender.clone()]))
.is_err()
{
panic!("Fatal error : fail to send all modules senders to all external_followers !");
}
}
// Subscribe all other modules to this module
if module_sender
.send(DuniterMessage::Followers(modules_senders.clone()))
.is_err()
{
panic!("Fatal error : fail to send all modules senders to all modules !");
}
// Subcribe all external_followers to this module
if module_sender
.send(DuniterMessage::Followers(external_followers.clone()))
.is_err()
{
panic!("Fatal error : fail to send all external_followers to all modules !");
}
// Push this module to modules_senders list
modules_senders.push(module_sender);
// Log the number of modules_senders received
info!(
"Rooter thread receive {} module senders",
modules_senders.len()
);
}
RooterThreadMessage::ModulesCount(modules_count) => {
info!("Rooter thread receive ModulesCount({})", modules_count);
if modules_senders.len() == modules_count {
break;
} else if modules_senders.len() < modules_count {
modules_count_expected = Some(modules_count);
} else {
panic!("Fatal error : Receive more modules_sender than expected !")
}
}
}
}
Err(e) => match e {
mpsc::RecvTimeoutError::Timeout => {
panic!("Fatal error : not receive all modules_senders after 20 secs !")
}
mpsc::RecvTimeoutError::Disconnected => {
panic!("Fatal error : rooter thread disconnnected !")
}
},
}
}
info!("Receive all modules senders.");
if run_duration_in_secs > 0 {
thread::sleep(Duration::from_secs(run_duration_in_secs));
// Send DuniterMessage::Stop() to all modules
for sender in modules_senders {
if sender.send(DuniterMessage::Stop()).is_err() {
panic!("Fail to send Stop() message to one module !")
}
}
thread::sleep(Duration::from_secs(2));
}
});
// Instanciate DuniterCore
DuniterCore {
start: true,
soft_name,
soft_version,
keypairs,
conf,
run_duration_in_secs,
rooter_sender,
modules_count: 0,
thread_pool: ThreadPool::new(2),
}
}
/// Launch synchronisation from a duniter-ts database
pub fn sync_ts(conf: DuniterConf, ts_profile: &str, cautious: bool) {
// Launch sync-ts
BlockchainModule::sync_ts(&conf, ts_profile, cautious);
}
/// Initialize logger
pub fn init_logger(profile: &str, soft_name: &'static str, cli_args: &ArgMatches) {
// Get datas folder path
let mut log_file_path = match env::home_dir() {
Some(path) => path,
None => panic!("Fatal error : Impossible to get your home dir!"),
};
log_file_path.push(".config");
if !log_file_path.as_path().exists() {
fs::create_dir(log_file_path.as_path()).expect("Impossible to create ~/.config dir !");
}
log_file_path.push(duniter_conf::get_user_datas_folder());
if !log_file_path.as_path().exists() {
fs::create_dir(log_file_path.as_path()).expect("Impossible to create ~/.config/durs dir !");
}
log_file_path.push(profile);
// Create datas folder if not exist
if !log_file_path.as_path().exists() {
fs::create_dir(log_file_path.as_path()).expect("Impossible to create your profile dir !");
}
// Get log_file_path
log_file_path.push(format!("{}.log", soft_name));
// Get log level
let log_level = match cli_args.value_of("logs").unwrap_or("i") {
"e" | "error" => Level::Error,
"w" | "warn" => Level::Warn,
"i" | "info" => Level::Info,
"d" | "debug" => Level::Debug,
"t" | "trace" => Level::Trace,
_ => panic!("Fatal error : unknow log level !"),
};
// Config logger
let logger_config = Config {
time: Some(Level::Error),
level: Some(Level::Error),
target: Some(Level::Debug),
location: Some(Level::Debug),
time_format: Some("%Y-%m-%d %H:%M:%S%:z"),
};
// Create log file if not exist
if !log_file_path.as_path().exists() {
File::create(
log_file_path
.to_str()
.expect("Fatal error : fail to get log file path !"),
).expect("Fatal error : fail to create log file path !");
}
CombinedLogger::init(vec![
TermLogger::new(LevelFilter::Error, logger_config).unwrap(),
WriteLogger::new(
log_level.to_level_filter(),
logger_config,
OpenOptions::new()
.write(true)
.append(true)
.open(
log_file_path
.to_str()
.expect("Fatal error : fail to get log file path !"),
)
.expect("Fatal error : fail to open log file !"),
),
]).expect("Fatal error : fail to init logger !");
}
[package]
name = "duniter-dal"
version = "0.1.0"
authors = ["librelois <elois@ifee.fr>"]
description = "Data Access Layer for the Duniter project."
license = "AGPL-3.0"
[lib]
path = "lib.rs"
[dependencies]
duniter-crypto = { path = "../crypto" }
duniter-documents = { path = "../documents" }
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"
rust-crypto = "0.2.36"
regex = "0.2.6"
sqlite = "0.23.9"
serde = "1.0.24"
serde_derive = "1.0.24"
serde_json = "1.0.9"
websocket = "0.20.2"
[features]
exp = []
# Treat warnings as a build error.
strict = []
\ No newline at end of file
This diff is collapsed.
#[derive(Debug, Copy, Clone)]
pub struct CurrencyParametersV10 {
pub c: f64,
pub dt: i64,
pub ud0: i64,
pub sig_period: u64,
pub sig_stock: i64,
pub sig_window: i64,
pub sig_validity: i64,
pub sig_qty: i64,
pub idty_window: i64,
pub ms_window: i64,
pub x_percent: f64,
pub ms_validity: u64,
pub step_max: u32,
pub median_time_blocks: i64,
pub avg_gen_time: i64,
pub dt_diff_eval: i64,
pub percent_rot: f64,
pub ud_time0: i64,
pub ud_reeval_time0: i64,
pub dt_reeval: i64,
}
pub static G1_PARAMS: &'static CurrencyParametersV10 = &CurrencyParametersV10 {
c: 0.0488,
dt: 86_400,
ud0: 1_000,
sig_period: 432_000,
sig_stock: 100,
sig_window: 5_259_600,
sig_validity: 63_115_200,
sig_qty: 5,
idty_window: 5_259_600,
ms_window: 5_259_600,
x_percent: 0.8,
ms_validity: 31_557_600,
step_max: 5,
median_time_blocks: 24,
avg_gen_time: 300,
dt_diff_eval: 12,
percent_rot: 0.67,
ud_time0: 1_488_970_800,
ud_reeval_time0: 1_490_094_000,
dt_reeval: 15_778_800,
};
pub static G1_CONNECTIVITY_MAX: &'static usize = &125;
pub static MAX_FORKS: &'static usize = &50;
extern crate duniter_documents;
extern crate serde;
use self::duniter_documents::blockchain::v10::documents::BlockDocument;
use self::duniter_documents::blockchain::BlockchainProtocol;
#[derive(Debug, Clone)]
pub enum DALEvent {
StackUpValidBlock(Box<BlockDocument>),
RevertBlocks(Vec<Box<BlockDocument>>),
NewValidPendingDoc(BlockchainProtocol),
RefusedPendingDoc(BlockchainProtocol),
}
extern crate duniter_crypto;
extern crate duniter_documents;
extern crate duniter_module;
extern crate serde;
use self::duniter_crypto::keys::ed25519;
use self::duniter_documents::blockchain::v10::documents::{
BlockDocument, CertificationDocument, IdentityDocument, MembershipDocument, RevocationDocument,
};
use self::duniter_documents::Hash;
use self::duniter_module::ModuleReqFullId;
use std::collections::HashMap;
#[derive(Debug, Copy, Clone)]
pub enum DALReqPendings {
AllPendingIdentyties(ModuleReqFullId, usize),
AllPendingIdentytiesWithoutCerts(ModuleReqFullId, usize),
PendingWotDatasForPubkey(ModuleReqFullId, ed25519::PublicKey),
}
#[derive(Debug, Clone, PartialEq)]
pub enum DALReqBlockchain {
CurrentBlock(ModuleReqFullId),
BlockByNumber(ModuleReqFullId, u64),
Chunk(ModuleReqFullId, u64, usize),
UIDs(Vec<ed25519::PublicKey>),
}
#[derive(Debug, Clone)]
pub enum DALRequest {
BlockchainRequest(DALReqBlockchain),
PendingsRequest(DALReqPendings),
}
#[derive(Debug, Clone)]
pub struct PendingIdtyDatas {
pub idty: IdentityDocument,
pub memberships: Vec<MembershipDocument>,
pub certs_count: usize,
pub certs: Vec<CertificationDocument>,
pub revocation: Option<RevocationDocument>,
}
#[derive(Debug, Clone)]
pub enum DALResPendings {
AllPendingIdentyties(HashMap<Hash, PendingIdtyDatas>),
AllPendingIdentytiesWithoutCerts(HashMap<Hash, PendingIdtyDatas>),
PendingWotDatasForPubkey(Vec<PendingIdtyDatas>),
}
#[derive(Debug, Clone)]
pub enum DALResBlockchain {
CurrentBlock(ModuleReqFullId, BlockDocument),
BlockByNumber(ModuleReqFullId, BlockDocument),
Chunk(ModuleReqFullId, Vec<BlockDocument>),
UIDs(HashMap<ed25519::PublicKey, Option<String>>),
}
#[derive(Debug, Clone)]
pub enum DALResponse {
Blockchain(DALResBlockchain),
Pendings(ModuleReqFullId, DALResPendings),
}
extern crate crypto;
extern crate duniter_crypto;
extern crate sqlite;
use std::time::Duration;
use self::crypto::digest::Digest;
use self::crypto::sha2::Sha256;
use self::duniter_crypto::keys::PublicKey;
use self::duniter_crypto::keys::ed25519::PublicKey as ed25519PublicKey;
use super::DuniterDB;
use super::WriteToDuniterDB;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DALEndpointApi {
WS2P,
//WS2PS,
//WS2PTOR,
//DASA,
//BMA,
//BMAS,
}
impl From<u32> for DALEndpointApi {
fn from(integer: u32) -> Self {
match integer {
_ => DALEndpointApi::WS2P,
}
}
}
pub fn string_to_api(api: &str) -> Option<DALEndpointApi> {
match api {
"WS2P" => Some(DALEndpointApi::WS2P),
//"WS2PS" => Some(DALEndpointApi::WS2PS),
//"WS2PTOR" => Some(DALEndpointApi::WS2PTOR),
//"DASA" => Some(DALEndpointApi::DASA),
//"BASIC_MERKLED_API" => Some(DALEndpointApi::BMA),
//"BMAS" => Some(DALEndpointApi::BMAS),
&_ => None,
}
}
pub fn api_to_integer(api: &DALEndpointApi) -> i64 {
match *api {
DALEndpointApi::WS2P => 0,
//DALEndpointApi::WS2PS => 1,
//DALEndpointApi::WS2PTOR => 2,
//DALEndpointApi::DASA => 3,
//DALEndpointApi::BMA => 4,
//DALEndpointApi::BMAS => 5,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DALEndpoint {
pub hash_full_id: String,
pub status: u32,
pub node_id: u32,
pub pubkey: ed25519PublicKey,
pub api: DALEndpointApi,
pub version: usize,
pub endpoint: String,
pub last_check: u64,
}
impl DALEndpoint {
pub fn new(
status: u32,
node_id: u32,
pubkey: ed25519PublicKey,
api: DALEndpointApi,
version: usize,
endpoint: String,
last_check: Duration,
) -> DALEndpoint {
let mut sha = Sha256::new();
sha.input_str(&format!(
"{}{}{}{}",
node_id,
pubkey,
api_to_integer(&api),
version
));
DALEndpoint {
hash_full_id: sha.result_str(),
status,
node_id,
pubkey,
api,
version,
endpoint,
last_check: last_check.as_secs(),
}
}
pub fn get_endpoints_for_api(db: &DuniterDB, api: DALEndpointApi) -> Vec<DALEndpoint> {
let mut cursor:sqlite::Cursor = db.0
.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()")
{
endpoints.push(DALEndpoint {
hash_full_id: row[0].as_string().unwrap().to_string(),
status: row[1].as_integer().unwrap() as u32,
node_id: row[2].as_integer().unwrap() as u32,
pubkey: ed25519PublicKey::from_base58(row[3].as_string().unwrap()).unwrap(),
api: DALEndpointApi::from(row[4].as_integer().unwrap() as u32),
version: row[5].as_integer().unwrap() as usize,
endpoint: row[6].as_string().unwrap().to_string(),
last_check: row[7].as_integer().unwrap() as u64,
});
}
endpoints
}
}
impl WriteToDuniterDB for DALEndpoint {
fn write(
&self,
db: &DuniterDB,
_written_blockstamp: super::block_v10::BlockStampV10,
_written_timestamp: u64,
) {
// Check if endpoint it's already written
let mut cursor: sqlite::Cursor = db.0
.prepare("SELECT status FROM endpoints WHERE hash_full_id=? ORDER BY status DESC;")
.expect("get_endpoints_for_api() : Error in SQL request !")
.cursor();
cursor
.bind(&[sqlite::Value::String(self.hash_full_id.clone())])
.expect("get_endpoints_for_api() : Error in cursor binding !");
// If endpoint it's already written, update status
if let Some(row) = cursor
.next()
.expect("get_endpoints_for_api() : Error in cursor.next()")
{
if row[0].as_integer().unwrap() as u32 != self.status {
db.0
.execute(format!(
"UPDATE endpoints SET status={} WHERE hash_full_id='{}'",
self.status, self.hash_full_id
))
.unwrap();
}
} else {
db.0
.execute(
format!(
"INSERT INTO endpoints (hash_full_id, status, node_id, pubkey, api, version, endpoint, last_check) VALUES ('{}', {}, {}, '{}', {}, {}, '{}', {});",
self.hash_full_id, self.status, self.node_id, self.pubkey.to_string(),
api_to_integer(&self.api), self.version, self.endpoint, self.last_check
)
)
.unwrap();
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment