diff --git a/Cargo.lock b/Cargo.lock index 2c5d16da462af2dd947e304887bd6de4ee131bc8..1f53e2c90a5a62e14d1933b46eee29a406fbd848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1571,6 +1571,7 @@ dependencies = [ "clap 4.1.4", "dubp-wot", "flate2", + "fnv", "num-traits", "parity-scale-codec", "rayon", @@ -2035,7 +2036,7 @@ checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.42.0", ] @@ -4287,9 +4288,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -5008,11 +5009,11 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" dependencies = [ - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -5813,7 +5814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -5825,22 +5826,22 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.1", ] [[package]] @@ -6462,6 +6463,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -6469,7 +6479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -9399,7 +9409,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "remove_dir_all", "winapi 0.3.9", ] @@ -10554,12 +10564,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] @@ -10569,7 +10579,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", ] [[package]] @@ -10578,21 +10588,42 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.34.0" @@ -10611,6 +10642,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.34.0" @@ -10629,6 +10666,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.34.0" @@ -10647,6 +10690,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.34.0" @@ -10665,12 +10714,24 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" @@ -10689,6 +10750,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" diff --git a/distance-oracle/Cargo.toml b/distance-oracle/Cargo.toml index 5e561323bed4466dca42e5f292d073a8636b988e..5f0b626ebd1a9eef6f09ee1e1889a7842b272b81 100644 --- a/distance-oracle/Cargo.toml +++ b/distance-oracle/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" sp-distance = { path = "../primitives/distance" } codec = { package = "parity-scale-codec", version = "3.1.5" } +fnv = "1.0.7" num-traits = "0.2.15" rayon = "1.7.0" sp-core = { git = "https://github.com/duniter/substrate.git", branch = "duniter-substrate-v0.9.32" } diff --git a/distance-oracle/src/api.rs b/distance-oracle/src/api.rs index 71bf77e7aa08aff93621ec5c98246d0484e6849a..0b007090dffb6a637163b88f1f8bca178ead3a4f 100644 --- a/distance-oracle/src/api.rs +++ b/distance-oracle/src/api.rs @@ -1,6 +1,22 @@ +// Copyright 2023 Axiom-Team +// +// This file is part of Duniter-v2S. +// +// Duniter-v2S 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, version 3 of the License. +// +// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. + use crate::{ - gdev, - gdev::runtime_types::{ + runtime, + runtime::runtime_types::{ pallet_distance::median::MedianAcc, sp_arithmetic::per_things::Perbill, sp_core::bounded::{bounded_btree_set::BoundedBTreeSet, bounded_vec::BoundedVec}, @@ -8,8 +24,9 @@ use crate::{ }; use sp_core::H256; +use subxt::storage::StorageKey; -pub type Client = subxt::OnlineClient<crate::GdevConfig>; +pub type Client = subxt::OnlineClient<crate::RuntimeConfig>; pub type AccountId = subxt::ext::sp_runtime::AccountId32; pub type IdtyIndex = u32; pub type EvaluationPool<AccountId, IdtyIndex> = ( @@ -24,7 +41,7 @@ pub async fn client(rpc_url: String) -> Client { pub async fn parent_hash(client: &Client) -> H256 { client .storage() - .fetch(&gdev::storage().system().parent_hash(), None) + .fetch(&runtime::storage().system().parent_hash(), None) .await .unwrap() .unwrap() @@ -34,7 +51,7 @@ pub async fn current_session(client: &Client, parent_hash: H256) -> u32 { client .storage() .fetch( - &gdev::storage().session().current_index(), + &runtime::storage().session().current_index(), Some(parent_hash), ) .await @@ -51,9 +68,9 @@ pub async fn current_pool( .storage() .fetch( &match current_session % 3 { - 0 => gdev::storage().distance().evaluation_pool1(), - 1 => gdev::storage().distance().evaluation_pool2(), - 2 => gdev::storage().distance().evaluation_pool0(), + 0 => runtime::storage().distance().evaluation_pool1(), + 1 => runtime::storage().distance().evaluation_pool2(), + 2 => runtime::storage().distance().evaluation_pool0(), _ => unreachable!("n%3<3"), }, Some(parent_hash), @@ -66,7 +83,7 @@ pub async fn evaluation_block(client: &Client, parent_hash: H256) -> H256 { client .storage() .fetch( - &gdev::storage().distance().evaluation_block(), + &runtime::storage().distance().evaluation_block(), Some(parent_hash), ) .await @@ -74,40 +91,74 @@ pub async fn evaluation_block(client: &Client, parent_hash: H256) -> H256 { .unwrap() } -pub async fn member_iter( - client: &Client, - evaluation_block: H256, -) -> subxt::storage::KeyIter< - crate::GdevConfig, - Client, - subxt::metadata::DecodeStaticType<gdev::runtime_types::sp_membership::MembershipData<u32>>, -> { - client - .storage() - .iter( - gdev::storage().membership().membership(0), - 100, - Some(evaluation_block), - ) - .await - .unwrap() +pub async fn member_iter(client: &Client, evaluation_block: H256) -> MemberIter { + MemberIter( + client + .storage() + .iter( + runtime::storage().membership().membership(0), + 100, + Some(evaluation_block), + ) + .await + .unwrap(), + ) } -pub async fn cert_iter( - client: &Client, - evaluation_block: H256, -) -> subxt::storage::KeyIter< - crate::GdevConfig, - Client, - subxt::metadata::DecodeStaticType<Vec<(u32, u32)>>, -> { - client - .storage() - .iter( - gdev::storage().cert().certs_by_receiver(0), - 100, - Some(evaluation_block), - ) - .await - .unwrap() +pub struct MemberIter( + subxt::storage::KeyIter< + crate::RuntimeConfig, + Client, + subxt::metadata::DecodeStaticType< + runtime::runtime_types::sp_membership::MembershipData<u32>, + >, + >, +); + +impl MemberIter { + pub async fn next(&mut self) -> Result<Option<IdtyIndex>, subxt::error::Error> { + Ok(self + .0 + .next() + .await? + .map(|(storage_key, _membership_data)| idty_id_from_storage_key(&storage_key))) + } +} + +pub async fn cert_iter(client: &Client, evaluation_block: H256) -> CertIter { + CertIter( + client + .storage() + .iter( + runtime::storage().cert().certs_by_receiver(0), + 100, + Some(evaluation_block), + ) + .await + .unwrap(), + ) +} + +pub struct CertIter( + subxt::storage::KeyIter< + crate::RuntimeConfig, + Client, + subxt::metadata::DecodeStaticType<Vec<(IdtyIndex, u32)>>, + >, +); + +impl CertIter { + pub async fn next( + &mut self, + ) -> Result<Option<(IdtyIndex, Vec<(IdtyIndex, u32)>)>, subxt::error::Error> { + Ok(self + .0 + .next() + .await? + .map(|(storage_key, issuers)| (idty_id_from_storage_key(&storage_key), issuers))) + } +} + +fn idty_id_from_storage_key(storage_key: &StorageKey) -> IdtyIndex { + u32::from_le_bytes(storage_key.as_ref()[40..44].try_into().unwrap()) } diff --git a/distance-oracle/src/lib.rs b/distance-oracle/src/lib.rs index 47418cc941771306158d9146b22cae2a8326c125..dc5c78b95fe42250fd8bead1f1832ad332fc5923 100644 --- a/distance-oracle/src/lib.rs +++ b/distance-oracle/src/lib.rs @@ -1,28 +1,44 @@ +// Copyright 2023 Axiom-Team +// +// This file is part of Duniter-v2S. +// +// Duniter-v2S 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, version 3 of the License. +// +// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. + #[cfg(not(test))] -mod api; +pub mod api; #[cfg(test)] -mod mock; +pub mod mock; #[cfg(test)] mod tests; #[cfg(test)] -use mock as api; +pub use mock as api; use api::{AccountId, IdtyIndex}; use codec::Encode; +use fnv::{FnvHashMap, FnvHashSet}; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; -use std::collections::{HashMap, HashSet}; use std::io::Write; use std::path::PathBuf; -use subxt::storage::StorageKey; +// TODO select metadata file using features #[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")] -pub mod gdev {} +pub mod runtime {} -pub enum GdevConfig {} -impl subxt::config::Config for GdevConfig { +pub enum RuntimeConfig {} +impl subxt::config::Config for RuntimeConfig { type Index = u32; type BlockNumber = u32; type Hash = sp_core::H256; @@ -71,8 +87,8 @@ impl Default for Settings { } } -pub async fn run_and_save(settings: Settings) { - let Some((evaluation, current_session, evaluation_result_path)) = run(&settings, true).await else {return}; +pub async fn run_and_save(client: &api::Client, settings: Settings) { + let Some((evaluation, current_session, evaluation_result_path)) = run(client, &settings, true).await else {return}; let mut evaluation_result_file = std::fs::OpenOptions::new() .write(true) @@ -108,17 +124,16 @@ pub async fn run_and_save(settings: Settings) { /// Returns `(evaluation, current_session, evaluation_result_path)` pub async fn run( + client: &api::Client, settings: &Settings, handle_fs: bool, ) -> Option<(Vec<sp_runtime::Perbill>, u32, PathBuf)> { - let client = api::client(settings.rpc_url.clone()).await; - - let parent_hash = api::parent_hash(&client).await; + let parent_hash = api::parent_hash(client).await; - let current_session = api::current_session(&client, parent_hash).await; + let current_session = api::current_session(client, parent_hash).await; // Fetch the pending identities - let Some(evaluation_pool) = api::current_pool(&client, parent_hash, current_session).await + let Some(evaluation_pool) = api::current_pool(client, parent_hash, current_session).await else { println!("Pool does not exist"); return None @@ -144,14 +159,14 @@ pub async fn run( std::fs::create_dir_all(&settings.evaluation_result_dir).unwrap(); } - let evaluation_block = api::evaluation_block(&client, parent_hash).await; + let evaluation_block = api::evaluation_block(client, parent_hash).await; // member idty -> issued certs - let mut members = HashMap::<IdtyIndex, u32>::new(); + let mut members = FnvHashMap::<IdtyIndex, u32>::default(); - let mut members_iter = api::member_iter(&client, evaluation_block).await; - while let Some((member_idty, _membership_expire)) = members_iter.next().await.unwrap() { - members.insert(idty_id_from_storage_key(&member_idty), 0); + let mut members_iter = api::member_iter(client, evaluation_block).await; + while let Some(member_idty) = members_iter.next().await.unwrap() { + members.insert(member_idty, 0); } let min_certs_for_referee = (members.len() as f32) @@ -159,22 +174,19 @@ pub async fn run( .ceil() as u32; // idty -> received certs - let mut received_certs = HashMap::<IdtyIndex, Vec<IdtyIndex>>::new(); + let mut received_certs = FnvHashMap::<IdtyIndex, Vec<IdtyIndex>>::default(); - let mut certs_iter = api::cert_iter(&client, evaluation_block).await; + let mut certs_iter = api::cert_iter(client, evaluation_block).await; while let Some((receiver, issuers)) = certs_iter.next().await.unwrap() { - let receiver = idty_id_from_storage_key(&receiver); - // Update members' issued certs count - if issuers.len() as u32 >= min_certs_for_referee { - for (issuer, _removable_on) in issuers.iter() { - if let Some(issued_certs) = members.get_mut(issuer) { - *issued_certs += 1; - } - } - } else { + if (issuers.len() as u32) < min_certs_for_referee { // This member is not referee members.remove(&receiver); } + for (issuer, _removable_on) in issuers.iter() { + if let Some(issued_certs) = members.get_mut(issuer) { + *issued_certs += 1; + } + } received_certs.insert( receiver, issuers @@ -185,7 +197,6 @@ pub async fn run( } // Only retain referees - // TODO benchmark: can it be faster? (maybe using drain_filter) members.retain(|_idty, issued_certs| *issued_certs >= min_certs_for_referee); let referees = members; @@ -194,66 +205,82 @@ pub async fn run( .0 .as_slice() .par_iter() - .map(|(idty, _)| { - sp_runtime::Perbill::from_rational( - distance_rule(&received_certs, &referees, settings.max_depth, *idty), - referees.len() as u32, - ) - }) + .map(|(idty, _)| distance_rule(&received_certs, &referees, settings.max_depth, *idty)) .collect(); Some((evaluation, current_session, evaluation_result_path)) } fn distance_rule_recursive( - received_certs: &HashMap<IdtyIndex, Vec<IdtyIndex>>, - referees: &HashMap<IdtyIndex, u32>, + received_certs: &FnvHashMap<IdtyIndex, Vec<IdtyIndex>>, + referees: &FnvHashMap<IdtyIndex, u32>, idty: IdtyIndex, - accessible_referees: &mut std::collections::HashSet<IdtyIndex>, + accessible_referees: &mut FnvHashSet<IdtyIndex>, + known_idties: &mut FnvHashMap<IdtyIndex, u32>, depth: u32, ) { + // Do not re-explore identities that have already been explored at least as deeply + match known_idties.entry(idty) { + std::collections::hash_map::Entry::Occupied(mut entry) => { + if *entry.get() >= depth { + return; + } else { + *entry.get_mut() = depth; + } + } + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(depth); + } + } + + // If referee, add it to the list if referees.contains_key(&idty) { accessible_referees.insert(idty); } + + // If reached the maximum distance, stop exploring if depth == 0 { return; } - for &certifier in received_certs.get(&idty).expect("unreachable").iter() { + + // Explore certifiers + for &certifier in received_certs.get(&idty).unwrap_or(&vec![]).iter() { distance_rule_recursive( received_certs, referees, certifier, accessible_referees, + known_idties, depth - 1, ); } } +/// Returns `(nb_accessible_referees, nb_referees)` fn distance_rule( - received_certs: &HashMap<IdtyIndex, Vec<IdtyIndex>>, - referees: &HashMap<IdtyIndex, u32>, + received_certs: &FnvHashMap<IdtyIndex, Vec<IdtyIndex>>, + referees: &FnvHashMap<IdtyIndex, u32>, depth: u32, idty: IdtyIndex, -) -> u32 { - let mut accessible_referees = HashSet::<u32>::new(); +) -> sp_runtime::Perbill { + let mut accessible_referees = + FnvHashSet::<IdtyIndex>::with_capacity_and_hasher(referees.len(), Default::default()); + let mut known_idties = + FnvHashMap::<IdtyIndex, u32>::with_capacity_and_hasher(referees.len(), Default::default()); distance_rule_recursive( received_certs, referees, idty, &mut accessible_referees, - depth + 1, + &mut known_idties, + depth, ); - accessible_referees.len() as u32 -} - -fn idty_id_from_storage_key(storage_key: &StorageKey) -> IdtyIndex { - u32::from_le_bytes(storage_key.as_ref()[40..44].try_into().unwrap()) + if referees.contains_key(&idty) { + sp_runtime::Perbill::from_rational( + accessible_referees.len() as u32 - 1, + referees.len() as u32 - 1, + ) + } else { + sp_runtime::Perbill::from_rational(accessible_referees.len() as u32, referees.len() as u32) + } } -/* -impl num_traits::Pow<usize> for Perbill {} -impl std::ops::Div<Perbill> for Perbill {} -impl num_traits::Bounded for Perbill {} -impl Ord for Perbill {} -impl Eq for Perbill {} - -impl PerThing for Perbill {}*/ diff --git a/distance-oracle/src/main.rs b/distance-oracle/src/main.rs index 5d25f621b95cd49673016f73d80952ff08a22e42..9d1c62340a0b9f13f5c81031970a089f826e2cf0 100644 --- a/distance-oracle/src/main.rs +++ b/distance-oracle/src/main.rs @@ -1,3 +1,19 @@ +// Copyright 2023 Axiom-Team +// +// This file is part of Duniter-v2S. +// +// Duniter-v2S 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, version 3 of the License. +// +// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. + use clap::Parser; #[derive(Debug, clap::Parser)] @@ -15,10 +31,13 @@ struct Cli { async fn main() { let cli = Cli::parse(); - distance_oracle::run_and_save(distance_oracle::Settings { - evaluation_result_dir: cli.evaluation_result_dir.into(), - max_depth: cli.max_depth, - rpc_url: cli.rpc_url, - }) + distance_oracle::run_and_save( + &distance_oracle::api::client(cli.rpc_url.clone()).await, + distance_oracle::Settings { + evaluation_result_dir: cli.evaluation_result_dir.into(), + max_depth: cli.max_depth, + rpc_url: cli.rpc_url, + }, + ) .await; } diff --git a/distance-oracle/src/mock.rs b/distance-oracle/src/mock.rs index faf20d0ef41b73e271c7c76606fae04282069754..54798d9761965ce4648a0961dcecc0cdbc3fec4b 100644 --- a/distance-oracle/src/mock.rs +++ b/distance-oracle/src/mock.rs @@ -1,18 +1,42 @@ -use crate::gdev::runtime_types::{ +// Copyright 2023 Axiom-Team +// +// This file is part of Duniter-v2S. +// +// Duniter-v2S 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, version 3 of the License. +// +// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. + +use crate::runtime::runtime_types::{ pallet_distance::median::MedianAcc, sp_arithmetic::per_things::Perbill, }; +use dubp_wot::{data::rusty::RustyWebOfTrust, WebOfTrust, WotId}; use sp_core::H256; use std::collections::BTreeSet; -pub struct Client; +pub struct Client { + wot: RustyWebOfTrust, + pub pool_len: usize, +} pub type AccountId = subxt::ext::sp_runtime::AccountId32; pub type IdtyIndex = u32; pub type EvaluationPool<AccountId, IdtyIndex> = ((Vec<(IdtyIndex, MedianAcc<Perbill>)>,), BTreeSet<AccountId>); pub async fn client(_rpc_url: String) -> Client { - Client + unimplemented!() +} + +pub fn client_from_wot(wot: RustyWebOfTrust) -> Client { + Client { wot, pool_len: 1 } } pub async fn parent_hash(_client: &Client) -> H256 { @@ -24,42 +48,71 @@ pub async fn current_session(_client: &Client, _parent_hash: H256) -> u32 { } pub async fn current_pool( - _client: &Client, + client: &Client, _parent_hash: H256, _current_session: u32, ) -> Option<EvaluationPool<AccountId, IdtyIndex>> { - Some(((vec![],), std::collections::BTreeSet::new())) + Some(( + (client + .wot + .get_enabled() + .into_iter() + .chain(client.wot.get_disabled().into_iter()) + .zip(0..client.pool_len) + .map(|(wot_id, _)| { + (wot_id.0 as IdtyIndex, unsafe { + std::mem::transmute((Vec::<()>::new(), Option::<u32>::None, 0)) + }) + }) + .collect(),), + std::collections::BTreeSet::new(), + )) } pub async fn evaluation_block(_client: &Client, _parent_hash: H256) -> H256 { Default::default() } -pub async fn member_iter( - _client: &Client, - _evaluation_block: H256, -) -> KeyIter<(subxt::storage::StorageKey, ())> { - KeyIter::new(Vec::new()) +pub async fn member_iter(client: &Client, _evaluation_block: H256) -> MemberIter { + MemberIter(client.wot.get_enabled().into_iter()) } -pub async fn cert_iter( - _client: &Client, - _evaluation_block: H256, -) -> KeyIter<(subxt::storage::StorageKey, Vec<(u32, u32)>)> { - KeyIter::new(Vec::new()) +pub struct MemberIter(std::vec::IntoIter<WotId>); + +impl MemberIter { + pub async fn next(&mut self) -> Result<Option<IdtyIndex>, subxt::error::Error> { + Ok(self.0.next().map(|wot_id| wot_id.0 as u32)) + } } -pub struct KeyIter<T>(std::vec::IntoIter<T>); +pub async fn cert_iter(client: &Client, _evaluation_block: H256) -> CertIter { + CertIter( + client + .wot + .get_enabled() + .iter() + .chain(client.wot.get_disabled().iter()) + .map(|wot_id| { + ( + wot_id.0 as IdtyIndex, + client + .wot + .get_links_source(*wot_id) + .unwrap_or_default() + .into_iter() + .map(|wot_id| (wot_id.0 as IdtyIndex, 0)) + .collect::<Vec<(IdtyIndex, u32)>>(), + ) + }) + .collect::<Vec<_>>() + .into_iter(), + ) +} -impl<T> KeyIter<T> { - fn new(items: Vec<T>) -> Self { - Self(items.into_iter()) - } +pub struct CertIter(std::vec::IntoIter<(IdtyIndex, Vec<(IdtyIndex, u32)>)>); - pub async fn next(&mut self) -> Result<Option<T>, subxt::error::Error> - where - T: Clone, - { +impl CertIter { + pub async fn next(&mut self) -> Result<Option<(u32, Vec<(u32, u32)>)>, subxt::error::Error> { Ok(self.0.next()) } } diff --git a/distance-oracle/src/tests.rs b/distance-oracle/src/tests.rs index 4c3f90fbe616d0483a3b4e7d266ce850332880b3..f2f6d77ed3ac38172f6f41efa3a4fc9042c3f580 100644 --- a/distance-oracle/src/tests.rs +++ b/distance-oracle/src/tests.rs @@ -1,12 +1,101 @@ -use dubp_wot::data::rusty::RustyWebOfTrust; +// Copyright 2023 Axiom-Team +// +// This file is part of Duniter-v2S. +// +// Duniter-v2S 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, version 3 of the License. +// +// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. + +use dubp_wot::{ + data::rusty::RustyWebOfTrust, operations::distance::DistanceCalculator, WebOfTrust, +}; use flate2::read::ZlibDecoder; +use sp_runtime::Perbill; use std::{fs::File, io::Read}; #[tokio::test] +#[ignore = "long to execute"] async fn test_distance_against_v1() { - let _wot = wot_from_v1_file(); + let wot = wot_from_v1_file(); + let n = wot.size(); + let max_depth = 5; + let min_certs_for_referee = (wot.get_enabled().len() as f32).powf(1. / 5.).ceil() as u32; + + // Reference implementation + let ref_calculator = dubp_wot::operations::distance::RustyDistanceCalculator; + let t_a = std::time::Instant::now(); + let ref_results: Vec<Perbill> = wot + .get_enabled() + .into_iter() + .chain(wot.get_disabled().into_iter()) + .zip(0..n) + .map(|(i, _)| { + let result = ref_calculator + .compute_distance( + &wot, + dubp_wot::operations::distance::WotDistanceParameters { + node: i, + sentry_requirement: min_certs_for_referee, + step_max: max_depth, + x_percent: 0.8, + }, + ) + .unwrap(); + Perbill::from_rational(result.success, result.sentries) + }) + .collect(); + println!("ref time: {}", t_a.elapsed().as_millis()); + + // Our implementation + let mut client = crate::api::client_from_wot(wot); + client.pool_len = n; + + let t_a = std::time::Instant::now(); + let results = crate::run( + &client, + &crate::Settings { + max_depth, + ..Default::default() + }, + false, + ) + .await + .unwrap(); + println!("new time: {}", t_a.elapsed().as_millis()); + assert_eq!(results.0.len(), n); + + let mut errors: Vec<_> = results + .0 + .iter() + .zip(ref_results.iter()) + .map(|(r, r_ref)| r.deconstruct() as i64 - r_ref.deconstruct() as i64) + .collect(); + errors.sort_unstable(); + println!( + "Error: {:?} / {:?} / {:?} / {:?} / {:?} (min / 1Q / med / 3Q / max)", + errors[0], + errors[errors.len() / 4], + errors[errors.len() / 2], + errors[errors.len() * 3 / 4], + errors[errors.len() - 1] + ); - crate::run(&Default::default(), false).await; + let correct_results = results + .0 + .iter() + .zip(ref_results.iter()) + .map(|(r, r_ref)| (r == r_ref) as usize) + .sum::<usize>(); + println!("Correct results: {correct_results} / {n}"); + assert_eq!(correct_results, n); } fn wot_from_v1_file() -> RustyWebOfTrust { diff --git a/end2end-tests/tests/common/distance.rs b/end2end-tests/tests/common/distance.rs index 15f9d61cee4bd9c4f94aa8f7b3750a70e1abb9b5..5b946ca93eb0a38a7c40feb7103ccf41398e03ca 100644 --- a/end2end-tests/tests/common/distance.rs +++ b/end2end-tests/tests/common/distance.rs @@ -46,6 +46,7 @@ pub async fn run_oracle(client: &Client, origin: AccountKeyring, rpc_url: String let account_id: &AccountId32 = origin.account_id(); if let Some((distances, _current_session, _evaluation_result_path)) = distance_oracle::run( + &distance_oracle::api::client(rpc_url.clone()).await, &distance_oracle::Settings { evaluation_result_dir: PathBuf::default(), max_depth: 5, @@ -64,21 +65,16 @@ pub async fn run_oracle(client: &Client, origin: AccountKeyring, rpc_url: String client .tx() .create_signed( - &gdev::tx().sudo().sudo(/*gdev::runtime_types::gdev_runtime::RuntimeCall::UpgradeOrigin(gdev::runtime_types::pallet_upgrade_origin::pallet::Call::dispatch_as_root { - call: Box::new(*/gdev::runtime_types::gdev_runtime::RuntimeCall::Distance( + &gdev::tx().sudo().sudo(gdev::runtime_types::gdev_runtime::RuntimeCall::Distance( gdev::runtime_types::pallet_distance::pallet::Call::force_update_evaluation { evaluator: account_id.clone(), computation_result: gdev::runtime_types::sp_distance::ComputationResult { - distances: unsafe { - std::mem::transmute(distances) - }, + distances: distances.into_iter().map(|res| unsafe{std::mem::transmute(res)}).collect(), }, }, - )/*) - })*/), - //&gdev::tx().upgrade_origin().dispatch_as_root( - //), + ) + ), &origin, BaseExtrinsicParamsBuilder::new(), ) diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs index 1712389a72c462c85d6dfc3e4f8ce6060fee758d..1c3d02f341f90d79b14afaafec72577cb8021f15 100644 --- a/end2end-tests/tests/cucumber_tests.rs +++ b/end2end-tests/tests/cucumber_tests.rs @@ -513,11 +513,7 @@ async fn should_have_distance_ok(world: &mut DuniterWorld, who: String) -> Resul .unwrap(); match world - .read( - &gdev::storage() - .distance() - .identities_distance_status(idty_id), - ) + .read(&gdev::storage().distance().identity_distance_status(idty_id)) .await? { Some(gdev::runtime_types::pallet_distance::types::DistanceStatus::Valid) => Ok(()), diff --git a/resources/metadata.scale b/resources/metadata.scale index 9582cd2af48d15f0121380317bb240fe5e4527a7..50671f6abfc29105bbfd0d68104bc525fde27b4f 100644 Binary files a/resources/metadata.scale and b/resources/metadata.scale differ