Mise à jour de GitLab prévue ce samedi 23 octobre 2021 à partir de 9h00 CET

Commit 628b91e0 authored by Éloïs's avatar Éloïs
Browse files

[ref] import dubp-wot crate in duniter repo

parent 3611f067
......@@ -131,9 +131,9 @@ checksum = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40"
[[package]]
name = "dubp-common"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aef0856c14d0ebd70bd577f3a85ab0bb08be080ee646a4c43d3e9c332bef9cac"
checksum = "8168403fac19c00a9556b84f5b22462fdc7ebf982f6d51a5844b5b5656e67343"
dependencies = [
"dup-crypto",
"serde",
......@@ -143,9 +143,9 @@ dependencies = [
[[package]]
name = "dubp-wot"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0f76dd4b734d34be5613c470402ccdb21cb21b84ecd75acdc81cf80c618201a"
dependencies = [
"bincode",
"dubp-common",
"log",
"rayon",
"serde",
......
[workspace]
members = ["neon/native"]
members = [
"neon/native",
"rust-libs/dubp-wot"
]
[patch.crates-io]
#dubp-common = { path = "../dubp-rs-libs/common" }
......@@ -17,7 +17,7 @@ neon-build = "0.4.0"
[dependencies]
bincode = "1.2.1"
bs58 = "0.3.0"
dubp-common = { version = "0.1.1", features = ["rand", "scrypt"] }
dubp-wot = "0.11.0"
dubp-common = { version = "0.1.2", features = ["rand", "scrypt"] }
dubp-wot = { path = "../../rust-libs/dubp-wot" }
flate2 = "1.0.16"
neon = "0.4.0"
[package]
name = "dubp-wot"
version = "0.11.0"
authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@duniter.org>"]
description = "Makes Web of Trust computations for the Duniter project."
repository = "https://git.duniter.org/nodes/typescript/duniter"
readme = "README.md"
keywords = ["duniter", "wot", "trust"]
license = "AGPL-3.0"
edition = "2018"
[lib]
path = "src/lib.rs"
[dependencies]
log = "0.4.8"
rayon = "1.3.0"
serde = { version = "1.0.105", features = ["derive"] }
[dev-dependencies]
bincode = "1.2.0"
dubp-common = { version = "0.1.2", features = ["rand", "scrypt"] }
[features]
# wot
`dubp-wot` is a crate making "Web of Trust" computations for
the [Duniter] project.
[Duniter]: https://duniter.org/en/
## How to use it
You can add `dubp-wot` as a `cargo` dependency in your Rust project.
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
//! Provide data structures to manage web of trusts.
//! `LegacyWebOfTrust` is almost a translation of the legacy C++ coden while
//! `RustyWebOfTrust` is a brand new implementation with a more "rusty" style.
pub mod rusty;
use serde::de::{self, Deserialize, DeserializeOwned, Deserializer, Visitor};
use serde::{Serialize, Serializer};
use std::{
fmt::{self, Debug},
io::Write,
};
/// Wrapper for a node id.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WotId(pub usize);
impl Serialize for WotId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u32(self.0 as u32)
}
}
struct WotIdVisitor;
impl<'de> Visitor<'de> for WotIdVisitor {
type Value = WotId;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an integer between -2^31 and 2^31")
}
fn visit_u8<E>(self, value: u8) -> Result<WotId, E>
where
E: de::Error,
{
Ok(WotId(value as usize))
}
fn visit_u32<E>(self, value: u32) -> Result<WotId, E>
where
E: de::Error,
{
Ok(WotId(value as usize))
}
fn visit_u64<E>(self, value: u64) -> Result<WotId, E>
where
E: de::Error,
{
use std::usize;
if value >= usize::MIN as u64 && value <= usize::MAX as u64 {
Ok(WotId(value as usize))
} else {
Err(E::custom(format!("u32 out of range: {}", value)))
}
}
}
impl<'de> Deserialize<'de> for WotId {
fn deserialize<D>(deserializer: D) -> Result<WotId, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u32(WotIdVisitor)
}
}
/// Results of a certification, with the current certification count
/// of the destination as parameter.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NewLinkResult {
/// Certification worked.
Ok(usize),
/// All available certifications has been used.
AllCertificationsUsed(usize),
/// Unknown source.
UnknownSource(),
/// Unknown target.
UnknownTarget(),
/// Self linking is forbidden.
SelfLinkingForbidden(),
}
/// Results of a certification removal, with the current certification count
/// of the destination as parameter.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RemLinkResult {
/// Certification has been removed.
Removed(usize),
/// Requested certification doesn't exist.
UnknownCert(usize),
/// Unknown source.
UnknownSource(),
/// Unknown target.
UnknownTarget(),
}
/// Results of a certification test.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HasLinkResult {
/// Both nodes are known, here is the result.
Link(bool),
/// Unknown source.
UnknownSource(),
/// Unknown target.
UnknownTarget(),
}
/// Trait for a Web Of Trust.
/// Allow to provide other implementations of the `WoT` logic instead of the legacy C++
/// translated one.
pub trait WebOfTrust: Clone + Debug + Default + DeserializeOwned + Send + Serialize + Sync {
/// Create a new Web of Trust with the maximum of links a node can issue.
fn new(max_links: usize) -> Self;
/// Clear Web of Trust datas
fn clear(&mut self);
/// Get the maximum number of links per user.
fn get_max_link(&self) -> usize;
/// Set the maximum number of links per user.
fn set_max_link(&mut self, max_link: usize);
/// Add a new node.
fn add_node(&mut self) -> WotId;
/// Remove the last node.
/// Returns `None` if the WoT was empty, otherwise new top node id.
fn rem_node(&mut self) -> Option<WotId>;
/// Get the size of the WoT.
fn size(&self) -> usize;
/// Check if given node is enabled.
/// Returns `None` if this node doesn't exist.
fn is_enabled(&self, id: WotId) -> Option<bool>;
/// Set the enabled state of given node.
/// Returns `Null` if this node doesn't exist, `enabled` otherwise.
fn set_enabled(&mut self, id: WotId, enabled: bool) -> Option<bool>;
/// Get enabled node array.
fn get_enabled(&self) -> Vec<WotId>;
/// Get disabled node array.
fn get_disabled(&self) -> Vec<WotId>;
/// Try to add a link from the source to the target.
fn add_link(&mut self, source: WotId, target: WotId) -> NewLinkResult;
/// Try to remove a link from the source to the target.
fn rem_link(&mut self, source: WotId, target: WotId) -> RemLinkResult;
/// Test if there is a link from the source to the target.
fn has_link(&self, source: WotId, target: WotId) -> HasLinkResult;
/// Get the list of links source for this target.
/// Returns `None` if this node doesn't exist.
fn get_links_source(&self, target: WotId) -> Option<Vec<WotId>>;
/// Get the number of issued links by a node.
/// Returns `None` if this node doesn't exist.
fn issued_count(&self, id: WotId) -> Option<usize>;
/// Test if a node is a sentry.
fn is_sentry(&self, node: WotId, sentry_requirement: usize) -> Option<bool>;
/// Get sentries array.
fn get_sentries(&self, sentry_requirement: usize) -> Vec<WotId>;
/// Get non sentries array.
fn get_non_sentries(&self, sentry_requirement: usize) -> Vec<WotId>;
/// Dump wot
fn dump<W: Write>(&self, output: &mut W) -> std::io::Result<()>;
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
//! Experimental implementation of the Web of Trust in a more "rusty" style.
use super::{HasLinkResult, NewLinkResult, RemLinkResult};
use crate::WebOfTrust;
use crate::WotId;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
/// A node in the `WoT` graph.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct Node {
/// Is this node enabled ?
enabled: bool,
/// Set of links this node is the target.
links_source: HashSet<WotId>,
/// Number of links the node issued.
issued_count: usize,
}
impl Node {
/// Create a new node.
pub fn new() -> Node {
Node {
enabled: true,
links_source: HashSet::new(),
issued_count: 0,
}
}
}
/// A more idiomatic implementation of a Web of Trust.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RustyWebOfTrust {
/// List of nodes in the WoT.
nodes: Vec<Node>,
/// Maximum number of links a node can issue.
max_links: usize,
}
impl Default for RustyWebOfTrust {
fn default() -> RustyWebOfTrust {
RustyWebOfTrust {
nodes: Vec::new(),
max_links: 4_000_000_000,
}
}
}
impl WebOfTrust for RustyWebOfTrust {
fn new(max_links: usize) -> RustyWebOfTrust {
RustyWebOfTrust {
nodes: vec![],
max_links,
}
}
fn clear(&mut self) {
self.nodes = Vec::new();
}
fn get_max_link(&self) -> usize {
self.max_links
}
fn set_max_link(&mut self, max_links: usize) {
self.max_links = max_links;
}
fn add_node(&mut self) -> WotId {
self.nodes.push(Node::new());
WotId(self.nodes.len() - 1)
}
fn rem_node(&mut self) -> Option<WotId> {
if !self.nodes.is_empty() {
self.nodes.pop();
Some(WotId(self.nodes.len()))
} else {
None
}
}
fn size(&self) -> usize {
self.nodes.len()
}
fn is_enabled(&self, id: WotId) -> Option<bool> {
self.nodes.get(id.0).map(|n| n.enabled)
}
fn set_enabled(&mut self, id: WotId, enabled: bool) -> Option<bool> {
self.nodes
.get_mut(id.0)
.map(|n| n.enabled = enabled)
.map(|_| enabled)
}
fn get_enabled(&self) -> Vec<WotId> {
self.nodes
.par_iter()
.enumerate()
.filter(|&(_, n)| n.enabled)
.map(|(i, _)| WotId(i))
.collect()
}
fn get_disabled(&self) -> Vec<WotId> {
self.nodes
.par_iter()
.enumerate()
.filter(|&(_, n)| !n.enabled)
.map(|(i, _)| WotId(i))
.collect()
}
fn add_link(&mut self, source: WotId, target: WotId) -> NewLinkResult {
if source == target {
NewLinkResult::SelfLinkingForbidden()
} else if source.0 >= self.size() {
NewLinkResult::UnknownSource()
} else if target.0 >= self.size() {
NewLinkResult::UnknownTarget()
} else if self.nodes[source.0].issued_count >= self.max_links {
NewLinkResult::AllCertificationsUsed(self.nodes[target.0].links_source.len())
} else {
self.nodes[source.0].issued_count += 1;
self.nodes[target.0].links_source.insert(source);
NewLinkResult::Ok(self.nodes[target.0].links_source.len())
}
}
fn rem_link(&mut self, source: WotId, target: WotId) -> RemLinkResult {
if source.0 >= self.size() {
RemLinkResult::UnknownSource()
} else if target.0 >= self.size() {
RemLinkResult::UnknownTarget()
} else if !self.nodes[target.0].links_source.contains(&source) {
RemLinkResult::UnknownCert(self.nodes[target.0].links_source.len())
} else {
self.nodes[source.0].issued_count -= 1;
self.nodes[target.0].links_source.remove(&source);
RemLinkResult::Removed(self.nodes[target.0].links_source.len())
}
}
fn has_link(&self, source: WotId, target: WotId) -> HasLinkResult {
if source.0 >= self.size() {
HasLinkResult::UnknownSource()
} else if target.0 >= self.size() {
HasLinkResult::UnknownTarget()
} else {
HasLinkResult::Link(self.nodes[target.0].links_source.contains(&source))
}
}
fn get_links_source(&self, target: WotId) -> Option<Vec<WotId>> {
self.nodes
.get(target.0)
.map(|n| n.links_source.iter().cloned().collect())
}
fn issued_count(&self, id: WotId) -> Option<usize> {
self.nodes.get(id.0).map(|n| n.issued_count)
}
fn is_sentry(&self, node: WotId, sentry_requirement: usize) -> Option<bool> {
if node.0 >= self.size() {
return None;
}
let node = &self.nodes[node.0];
Some(
node.enabled
&& node.issued_count >= sentry_requirement
&& node.links_source.len() >= sentry_requirement,
)
}
fn get_sentries(&self, sentry_requirement: usize) -> Vec<WotId> {
self.nodes
.par_iter()
.enumerate()
.filter(|&(_, n)| {
n.enabled
&& n.issued_count >= sentry_requirement
&& n.links_source.len() >= sentry_requirement
})
.map(|(i, _)| WotId(i))
.collect()
}
fn get_non_sentries(&self, sentry_requirement: usize) -> Vec<WotId> {
self.nodes
.par_iter()
.enumerate()
.filter(|&(_, n)| {
n.enabled
&& (n.issued_count < sentry_requirement
|| n.links_source.len() < sentry_requirement)
})
.map(|(i, _)| WotId(i))
.collect()
}
fn dump<W: std::io::Write>(&self, output: &mut W) -> std::io::Result<()> {
writeln!(output, "max_links={}", self.max_links)?;
writeln!(output, "nodes_count={}", self.nodes.len())?;
for (node_id, node) in self.nodes.iter().enumerate() {
write!(output, "{:03}: ", node_id)?;
if !node.enabled {
write!(output, "disabled ")?;
}
// dump sources
write!(output, "[")?;
let mut sorted_sources = node.links_source.iter().copied().collect::<Vec<WotId>>();
sorted_sources.sort_unstable();
let mut remaining_sources = sorted_sources.len();
for source in &sorted_sources {
if remaining_sources == 1 {
write!(output, "{}", source.0)?;
} else {
write!(output, "{}, ", source.0)?;
remaining_sources -= 1;
}
}
writeln!(output, "]")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::generic_wot_test;
#[test]
fn wot_tests() {
generic_wot_test::<RustyWebOfTrust>();
}
}
// Copyright (C) 2017-2020 The AXIOM TEAM Association.
//
// 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/>.
//! `wot` is a crate making "Web of Trust" computations for
//! the [Duniter] project.
//!
//! [Duniter]: https://duniter.org/
//!
//! It defines a trait representing a Web of Trust and allow to do calculations on it.
//!
//! It also contains an "legacy" implementation translated from the original C++ code.
//!
//! Web of Trust tests are translated from [duniter/wot Javascript test][js-tests].
//!
//! [js-tests]: https://github.com/duniter/wot/blob/master/wotcpp/webOfTrust.cpp
#![deny(
clippy::unwrap_used,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
pub mod data;
pub mod operations;
pub use crate::data::{WebOfTrust, WotId};
#[cfg(test)]
mod tests {
use super::*;
use crate::data::*;
use crate::operations::centrality::*;
use crate::operations::distance::*;
use crate::operations::path::*;
use std::path::Path;
/// Test translated from https://github.com/duniter/wot/blob/master/tests/test.js
///
/// Clone and file tests are not included in this generic test and should be done in
/// the implementation test.
#[allow(clippy::cognitive_complexity)]
pub fn generic_wot_test<W>()
where
W: WebOfTrust + Sync,
{
let centralities_calculator = UlrikBrandesCentralityCalculator {};
let distance_calculator = RustyDistanceCalculator {};
let path_finder = RustyPathFinder {};