diff --git a/Cargo.lock b/Cargo.lock index a38e386296b9761631a3b2817111b0e1afa37f0b..e4cbdab308701d3599a2dfc6aac9ec857905a5a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,6 +653,12 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cc-traits" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "becb23f44ae9dd141d637d520b908c291bf4c5eed016ca368daa1430d54ebf4c" + [[package]] name = "cfg-expr" version = "0.10.3" @@ -1558,7 +1564,9 @@ dependencies = [ name = "distance-oracle" version = "0.1.0" dependencies = [ + "cc-traits", "clap 4.1.4", + "median-accumulator", "parity-scale-codec", "rayon", "sp-core", @@ -4367,6 +4375,15 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "median-accumulator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9777055bcc453173875aab61f856474ec399120e8828fd02093841fc602eb73a" +dependencies = [ + "cc-traits", +] + [[package]] name = "memchr" version = "2.5.0" @@ -5203,8 +5220,10 @@ dependencies = [ name = "pallet-distance" version = "1.0.0" dependencies = [ + "cc-traits", "frame-support", "frame-system", + "median-accumulator", "pallet-authority-members", "pallet-authorship", "pallet-certification", @@ -9798,7 +9817,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "digest 0.10.6", "rand 0.8.5", "static_assertions", diff --git a/distance-oracle/Cargo.toml b/distance-oracle/Cargo.toml index 8869a5fc6b1901d53eb0bd49904822139890f497..8c80fb6f6b34dab1d4ed36a9d61eb00cc1f16d56 100644 --- a/distance-oracle/Cargo.toml +++ b/distance-oracle/Cargo.toml @@ -9,7 +9,9 @@ edition = "2021" [dependencies] sp-distance = { path = "../primitives/distance" } +cc-traits = "1.0.0" codec = { package = "parity-scale-codec", version = "3.1.5" } +median-accumulator = { version = "0.2.0", features = ["nostd"] } rayon = "1.7.0" sp-core = { git = "https://github.com/duniter/substrate.git", branch = "duniter-substrate-v0.9.32" } sp-runtime = { 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 new file mode 100644 index 0000000000000000000000000000000000000000..6a177aeebad18bbc64ffd0f917e1b9a919aefed3 --- /dev/null +++ b/distance-oracle/src/api.rs @@ -0,0 +1,98 @@ +use crate::{AccountId, EvaluationPool, IdtyIndex}; + +use codec::Encode; +use sp_core::H256; +use subxt::ext::sp_runtime::Perbill; +use subxt::storage::StorageKey; + +#[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")] +pub mod gdev {} + +pub type Client = subxt::OnlineClient<GdevConfig>; + +pub enum GdevConfig {} +impl subxt::config::Config for GdevConfig { + type Index = u32; + type BlockNumber = u32; + type Hash = sp_core::H256; + type Hashing = subxt::ext::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Address = subxt::ext::sp_runtime::MultiAddress<Self::AccountId, u32>; + type Header = subxt::ext::sp_runtime::generic::Header< + Self::BlockNumber, + subxt::ext::sp_runtime::traits::BlakeTwo256, + >; + type Signature = subxt::ext::sp_runtime::MultiSignature; + type ExtrinsicParams = subxt::tx::BaseExtrinsicParams<Self, Tip>; +} + +#[derive(Copy, Clone, Debug, Default, Encode)] +pub struct Tip { + #[codec(compact)] + tip: u64, +} + +impl Tip { + pub fn new(amount: u64) -> Self { + Tip { tip: amount } + } +} + +impl From<u64> for Tip { + fn from(n: u64) -> Self { + Self::new(n) + } +} + +pub async fn parent_hash(client: &Client) -> H256 { + client + .storage() + .fetch(&gdev::storage().system().parent_hash(), None) + .await + .unwrap() + .unwrap() +} + +pub async fn current_session(client: &Client, parent_hash: H256) -> u32 { + client + .storage() + .fetch( + &gdev::storage().session().current_index(), + Some(parent_hash), + ) + .await + .unwrap() + .unwrap_or_default() +} + +pub async fn current_pool<MaxEvaluationsPerSession, MaxEvaluatorsPerSession>( + client: &Client, + parent_hash: H256, + current_session: u32, +) -> Option<EvaluationPool<AccountId, IdtyIndex, MaxEvaluationsPerSession, MaxEvaluatorsPerSession>> +{ + client + .storage() + .fetch( + &match current_session % 3 { + 0 => gdev::storage().distance().evaluation_pool1(), + 1 => gdev::storage().distance().evaluation_pool2(), + 2 => gdev::storage().distance().evaluation_pool0(), + _ => unreachable!("n%3<3"), + }, + Some(parent_hash), + ) + .await +} + +pub async fn evaluation_block(client: &Client, parent_hash: H256) -> H256 { + client + .storage() + .fetch( + &gdev::storage().distance().evaluation_block(), + Some(parent_hash), + ) + .await + .unwrap() + .unwrap() +} diff --git a/distance-oracle/src/impls.rs b/distance-oracle/src/impls.rs new file mode 100644 index 0000000000000000000000000000000000000000..7ceed4f23388a8b780928b4f1c8ec36350b28413 --- /dev/null +++ b/distance-oracle/src/impls.rs @@ -0,0 +1,7 @@ +impl<T, S> cc_traits::GetMut<T> for sp_core::bounded::BoundedVec<T, S> {} + +impl<T, S> cc_traits::Len for sp_core::bounded::BoundedVec<T, S> { + fn len(&self) -> usize { + self.len() + } +} diff --git a/distance-oracle/src/lib.rs b/distance-oracle/src/lib.rs index 62fc97b5d772ef81d36ce479684702360ec02e22..83d94f5f23039bca6fab7a7faa5c3f4b105ca725 100644 --- a/distance-oracle/src/lib.rs +++ b/distance-oracle/src/lib.rs @@ -1,5 +1,18 @@ +#[cfg(not(test))] +mod api; +mod impls; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +#[cfg(not(test))] +use api::{gdev, Client}; + use codec::Encode; +use median_accumulator::*; use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use sp_core::bounded::{BoundedBTreeSet, BoundedVec}; use std::collections::{HashMap, HashSet}; use std::io::Write; use std::path::PathBuf; @@ -22,80 +35,29 @@ impl Default for Settings { } } -#[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")] -pub mod gdev {} - -pub type Client = subxt::OnlineClient<GdevConfig>; - -pub enum GdevConfig {} -impl subxt::config::Config for GdevConfig { - type Index = u32; - type BlockNumber = u32; - type Hash = sp_core::H256; - type Hashing = subxt::ext::sp_runtime::traits::BlakeTwo256; - type AccountId = subxt::ext::sp_runtime::AccountId32; - type Address = subxt::ext::sp_runtime::MultiAddress<Self::AccountId, u32>; - type Header = subxt::ext::sp_runtime::generic::Header< - Self::BlockNumber, - subxt::ext::sp_runtime::traits::BlakeTwo256, - >; - type Signature = subxt::ext::sp_runtime::MultiSignature; - type ExtrinsicParams = subxt::tx::BaseExtrinsicParams<Self, Tip>; -} - -#[derive(Copy, Clone, Debug, Default, Encode)] -pub struct Tip { - #[codec(compact)] - tip: u64, -} - -impl Tip { - pub fn new(amount: u64) -> Self { - Tip { tip: amount } - } -} - -impl From<u64> for Tip { - fn from(n: u64) -> Self { - Self::new(n) - } -} - +type AccountId = subxt::ext::sp_runtime::AccountId32; type IdtyIndex = u32; +type EvaluationPool<AccountId, IdtyIndex, MaxEvaluationsPerSession, MaxEvaluatorsPerSession> = ( + BoundedVec< + ( + IdtyIndex, + MedianAcc<Perbill, BoundedVec<Perbill, MaxEvaluatorsPerSession>>, + ), + MaxEvaluationsPerSession, + >, + BoundedBTreeSet<AccountId, MaxEvaluatorsPerSession>, +); + pub async fn run(settings: Settings) { let client = Client::from_url(settings.rpc_url).await.unwrap(); - let parent_hash = client - .storage() - .fetch(&gdev::storage().system().parent_hash(), None) - .await - .unwrap() - .unwrap(); + let parent_hash = api::parent_hash(&client).await; - let current_session = client - .storage() - .fetch( - &gdev::storage().session().current_index(), - Some(parent_hash), - ) - .await - .unwrap() - .unwrap_or_default(); + let current_session = api::current_session(&client, parent_hash).await; // Fetch the pending identities - let Some(evaluation_pool) = client - .storage() - .fetch( - &match current_session % 3 { - 0 => gdev::storage().distance().evaluation_pool1(), - 1 => gdev::storage().distance().evaluation_pool2(), - 2 => gdev::storage().distance().evaluation_pool0(), - _ => unreachable!("n%3<3"), - }, - Some(parent_hash), - ) - .await + let Some(evaluation_pool) = api::current_pool(&client, parent_hash, current_session).await .unwrap() else { println!("Pool does not exist"); return @@ -117,15 +79,7 @@ pub async fn run(settings: Settings) { return; } - let evaluation_block = client - .storage() - .fetch( - &gdev::storage().distance().evaluation_block(), - Some(parent_hash), - ) - .await - .unwrap() - .unwrap(); + let evaluation_block = api::evaluation_block(&client, parent_hash).await; std::fs::create_dir_all(&settings.evaluation_result_dir).unwrap(); diff --git a/distance-oracle/src/mock.rs b/distance-oracle/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/distance-oracle/src/mock.rs @@ -0,0 +1 @@ + diff --git a/distance-oracle/src/tests.rs b/distance-oracle/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..0c833b61a9f4587b93a5f94f74e61c210e28d1e3 --- /dev/null +++ b/distance-oracle/src/tests.rs @@ -0,0 +1,2 @@ +#[test] +fn test_distance_validity() {} diff --git a/pallets/distance/Cargo.toml b/pallets/distance/Cargo.toml index 47f1f67105a70867e2ab8494eb6bdfd7ed0cc506..b2cb197a9fa0959eae518e369c26273e65dde624 100644 --- a/pallets/distance/Cargo.toml +++ b/pallets/distance/Cargo.toml @@ -34,6 +34,9 @@ pallet-identity = { path = "../identity", default-features = false } pallet-membership = { path = "../membership", default-features = false } sp-distance = { path = "../../primitives/distance", default-features = false } +cc-traits = "1.0.0" +median-accumulator = { version = "0.2.0", features = ["nostd"] } + # substrate scale-info = { version = "2.1.1", default-features = false, features = [ "derive", diff --git a/pallets/distance/src/lib.rs b/pallets/distance/src/lib.rs index cce777e7d6d3e43a76b6135c2534774ab1497c9c..09c38cbb248019d563b5714388ae617bf466b740 100644 --- a/pallets/distance/src/lib.rs +++ b/pallets/distance/src/lib.rs @@ -26,6 +26,7 @@ pub use traits::*; pub use types::*; use frame_support::traits::StorageVersion; +//use median_accumulator::*; use pallet_authority_members::SessionIndex; use sp_distance::{InherentError, INHERENT_IDENTIFIER}; use sp_inherents::{InherentData, InherentIdentifier}; @@ -65,7 +66,7 @@ pub mod pallet { /// Maximum number of identities to be evaluated in a session type MaxEvaluationsPerSession: Get<u32>; /// Maximum number of evaluators in a session - type MaxEvaluatorsPerSession: Get<u32>; + type MaxEvaluatorsPerSession: Get<u32> + TypeInfo; /// Minimum ratio of accessible referees type MinAccessibleReferees: Get<Perbill>; // /// Handler for the evaluation price in case of negative result @@ -74,17 +75,8 @@ pub mod pallet { // STORAGE // - pub type EvaluationPool< - AccountId, - IdtyIndex, - MaxEvaluationsPerSession, - MaxEvaluatorsPerSession, - > = ( - BoundedVec<(IdtyIndex, MedianAcc<Perbill>), MaxEvaluationsPerSession>, - BoundedBTreeSet<AccountId, MaxEvaluatorsPerSession>, - ); - #[pallet::storage] + #[pallet::getter(fn evaluation_pool_0)] pub type EvaluationPool0<T: Config<I>, I: 'static = ()> = StorageValue< _, EvaluationPool< @@ -96,6 +88,7 @@ pub mod pallet { ValueQuery, >; #[pallet::storage] + #[pallet::getter(fn evaluation_pool_1)] pub type EvaluationPool1<T: Config<I>, I: 'static = ()> = StorageValue< _, EvaluationPool< @@ -107,6 +100,7 @@ pub mod pallet { ValueQuery, >; #[pallet::storage] + #[pallet::getter(fn evaluation_pool_2)] pub type EvaluationPool2<T: Config<I>, I: 'static = ()> = StorageValue< _, EvaluationPool< diff --git a/pallets/distance/src/median.rs b/pallets/distance/src/median.rs index 065efb3119377cea4906e86c45e176f56f1b1941..a5f078d781c5d522abea2ddd65cef0882fa9fb97 100644 --- a/pallets/distance/src/median.rs +++ b/pallets/distance/src/median.rs @@ -14,12 +14,12 @@ // 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 frame_support::pallet_prelude::*; +use frame_support::{pallet_prelude::*, traits::*}; use sp_std::{cmp::Ordering, vec::Vec}; #[derive(Clone, Debug, Decode, Default, Encode, TypeInfo)] -pub struct MedianAcc<T: Clone + Decode + Encode + Ord + TypeInfo> { - samples: Vec<(T, u32)>, +pub struct MedianAcc<T: Clone + Decode + Encode + Ord + TypeInfo, S: Get<u32> + TypeInfo> { + samples: BoundedVec<(T, u32), S>, median_index: Option<u32>, median_subindex: u32, } @@ -30,10 +30,10 @@ pub enum MedianResult<T: Clone + Ord> { Two(T, T), } -impl<T: Clone + Decode + Encode + Ord + TypeInfo> MedianAcc<T> { +impl<T: Clone + Decode + Encode + Ord + TypeInfo, S: Get<u32> + TypeInfo> MedianAcc<T, S> { pub fn new() -> Self { Self { - samples: Vec::new(), + samples: BoundedVec::default(), median_index: None, median_subindex: 0, } @@ -84,7 +84,7 @@ impl<T: Clone + Decode + Encode + Ord + TypeInfo> MedianAcc<T> { } } Err(sample_index) => { - self.samples.insert(sample_index, (sample, 1)); + self.samples.try_insert(sample_index, (sample, 1)); if *median_index as usize >= sample_index { if self.median_subindex == 0 { self.median_subindex = self @@ -115,7 +115,7 @@ impl<T: Clone + Decode + Encode + Ord + TypeInfo> MedianAcc<T> { } } } else { - self.samples.push((sample, 1)); + self.samples.try_push((sample, 1)); self.median_index = Some(0); } } diff --git a/pallets/distance/src/types.rs b/pallets/distance/src/types.rs index 2d3edf946f670bc274f11519046e2a32fb3ea6ea..ab629ec482290350863cadfb8a85de3273a67b13 100644 --- a/pallets/distance/src/types.rs +++ b/pallets/distance/src/types.rs @@ -14,10 +14,13 @@ // 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/>. +pub use crate::median::*; pub use sp_distance::ComputationResult; use codec::{Decode, Encode}; -use frame_support::pallet_prelude::*; +use frame_support::{pallet_prelude::*, BoundedBTreeSet}; +//use median_accumulator::*; +use sp_runtime::Perbill; #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum DistanceStatus { @@ -26,3 +29,18 @@ pub enum DistanceStatus { /// Identity respects the distance Valid, } + +pub type EvaluationPool<AccountId, IdtyIndex, MaxEvaluationsPerSession, MaxEvaluatorsPerSession> = ( + BoundedVec<(IdtyIndex, MedianAcc<Perbill, MaxEvaluatorsPerSession>), MaxEvaluationsPerSession>, + BoundedBTreeSet<AccountId, MaxEvaluatorsPerSession>, +); + +/*pub struct BoundedVecC<T, S>(BoundedVec<T, S>); + +impl<T, S> cc_traits::GetMut<T> for BoundedVecC<T, S> {} + +impl<T, S> cc_traits::Len for BoundedVecC<T, S> { + fn len(&self) -> usize { + self.0.len() + } +}*/ diff --git a/resources/metadata.scale b/resources/metadata.scale index c5d992edbe02769551ed0a8ba738d79232bccfbe..d877488391b6849628755f61564641ad0e10c5e8 100644 Binary files a/resources/metadata.scale and b/resources/metadata.scale differ