From 47256e5754e15a325d2682804f459c90bb1429e5 Mon Sep 17 00:00:00 2001
From: tuxmain <tuxmain@zettascript.org>
Date: Mon, 19 Jun 2023 14:56:38 +0200
Subject: [PATCH] wip

---
 Cargo.lock                     |  21 ++++++-
 distance-oracle/Cargo.toml     |   2 +
 distance-oracle/src/api.rs     |  98 +++++++++++++++++++++++++++++++
 distance-oracle/src/impls.rs   |   7 +++
 distance-oracle/src/lib.rs     | 104 +++++++++------------------------
 distance-oracle/src/mock.rs    |   1 +
 distance-oracle/src/tests.rs   |   2 +
 pallets/distance/Cargo.toml    |   3 +
 pallets/distance/src/lib.rs    |  16 ++---
 pallets/distance/src/median.rs |  14 ++---
 pallets/distance/src/types.rs  |  20 ++++++-
 resources/metadata.scale       | Bin 132872 -> 132702 bytes
 12 files changed, 193 insertions(+), 95 deletions(-)
 create mode 100644 distance-oracle/src/api.rs
 create mode 100644 distance-oracle/src/impls.rs
 create mode 100644 distance-oracle/src/mock.rs
 create mode 100644 distance-oracle/src/tests.rs

diff --git a/Cargo.lock b/Cargo.lock
index a38e38629..e4cbdab30 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 8869a5fc6..8c80fb6f6 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 000000000..6a177aeeb
--- /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 000000000..7ceed4f23
--- /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 62fc97b5d..83d94f5f2 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 000000000..8b1378917
--- /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 000000000..0c833b61a
--- /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 47f1f6710..b2cb197a9 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 cce777e7d..09c38cbb2 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 065efb311..a5f078d78 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 2d3edf946..ab629ec48 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
GIT binary patch
delta 7812
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

delta 7670
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

-- 
GitLab