diff --git a/Cargo.lock b/Cargo.lock index e2b6bba2a56bdd125475db9f94084b56e52c89d6..8d2c3d8ceddaddf008d626ab7f5f3d342c853168 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,6 +819,8 @@ dependencies = [ "pallet-babe", "pallet-balances", "pallet-certification", + "pallet-distance", + "pallet-distance-rpc-runtime-api", "pallet-duniter-account", "pallet-duniter-wot", "pallet-grandpa", @@ -1472,6 +1474,8 @@ dependencies = [ "maplit", "memmap2 0.5.0", "pallet-certification", + "pallet-distance-rpc", + "pallet-distance-rpc-runtime-api", "pallet-grandpa", "pallet-transaction-payment-rpc", "pallet-transaction-payment-rpc-runtime-api", @@ -2419,6 +2423,8 @@ dependencies = [ "pallet-balances", "pallet-certification", "pallet-collective", + "pallet-distance", + "pallet-distance-rpc-runtime-api", "pallet-duniter-account", "pallet-duniter-test-parameters", "pallet-duniter-wot", @@ -2905,13 +2911,13 @@ dependencies = [ [[package]] name = "http" -version = "0.2.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 0.4.7", + "itoa 1.0.1", ] [[package]] @@ -5028,6 +5034,55 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-distance" +version = "1.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-authority-members", + "pallet-certification", + "pallet-identity", + "pallet-membership", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-distance-rpc" +version = "1.0.0" +dependencies = [ + "frame-support", + "frame-system", + "jsonrpsee 0.14.0", + "pallet-distance-rpc-runtime-api", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-distance-rpc-runtime-api" +version = "1.0.0" +dependencies = [ + "frame-support", + "pallet-distance", + "parity-scale-codec", + "sp-api", + "sp-runtime", +] + [[package]] name = "pallet-duniter-account" version = "3.0.0" @@ -9374,9 +9429,9 @@ 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.3", - "rand 0.7.3", + "rand 0.8.4", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index 16e11ef4537fe2323835a5ff30f6225e77070f4e..209751d76706e71b505a8f0b5b933fd41bdc04d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,8 @@ g1-runtime = { path = 'runtime/g1', optional = true } gdev-runtime = { path = 'runtime/gdev', optional = true } gtest-runtime = { path = 'runtime/gtest', optional = true } pallet-certification = { path = 'pallets/certification' } +pallet-distance-rpc = { path = 'pallets/distance/rpc' } +pallet-distance-rpc-runtime-api = { path = 'pallets/distance/rpc/runtime-api' } sp-membership = { path = 'primitives/membership' } # crates.io dependencies @@ -128,6 +130,9 @@ members = [ 'end2end-tests', 'live-tests', 'pallets/certification', + 'pallets/distance', + 'pallets/distance/rpc', + 'pallets/distance/rpc/runtime-api', 'pallets/duniter-test-parameters', 'pallets/duniter-test-parameters/macro', 'pallets/duniter-wot', diff --git a/node/src/rpc.rs b/node/src/rpc.rs index 36dee008a8e037c36cd0b4430a120189b878fb2d..492f59fc048d893b2db2ec82768fce04ff136687 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -56,6 +56,7 @@ where C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>, C::Api: BlockBuilder<Block>, + //C::Api: pallet_distance_runtime_api::DistanceApi<Block>, P: TransactionPool + 'static, { use manual_seal::rpc::{ManualSeal, ManualSealApiServer}; @@ -83,6 +84,9 @@ where // `YourRpcStruct` should have a reference to a client, which is needed // to call into the runtime. // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;` + /*module.merge(pallet_distance_rpc::DistanceApi::into_rpc( + pallet_distance_rpc::Distance::new(client), + ))?;*/ Ok(module) } diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs index 941a0af7d56e1f4be886df51bc7905436b93f900..2d052c75fadd1882893c14e2ea393b7a0ff6474f 100644 --- a/pallets/authority-members/src/lib.rs +++ b/pallets/authority-members/src/lib.rs @@ -29,13 +29,13 @@ mod tests; /*#[cfg(feature = "runtime-benchmarks")] mod benchmarking;*/ +pub use self::traits::*; pub use pallet::*; +pub use sp_staking::SessionIndex; pub use types::*; -use self::traits::*; use frame_support::traits::Get; use sp_runtime::traits::Convert; -use sp_staking::SessionIndex; use sp_std::prelude::*; #[frame_support::pallet] diff --git a/pallets/distance/Cargo.toml b/pallets/distance/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..75ae1ef9a7210ca44ea7f12be96fe100d707168f --- /dev/null +++ b/pallets/distance/Cargo.toml @@ -0,0 +1,78 @@ +[package] +authors = ['tuxmain <tuxmain@zettascript.org>'] +description = 'FRAME pallet distance.' +edition = '2021' +homepage = 'https://duniter.org' +license = 'AGPL-3.0' +name = 'pallet-distance' +readme = 'README.md' +repository = 'https://git.duniter.org/nodes/rust/duniter-v2s' +version = '1.0.0' + +[features] +default = ['std'] +std = [ + 'codec/std', + 'frame-support/std', + 'frame-system/std', + 'pallet-session/std', + 'sp-core/std', + 'sp-runtime/std', + 'sp-std/std', +] + +[dependencies] + +pallet-authority-members = { path = "../authority-members", default-features = false } +pallet-certification = { path = "../certification", default-features = false } +pallet-identity = { path = "../identity", default-features = false } +pallet-membership = { path = "../membership", default-features = false } + +# substrate +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '3.1.5' + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.pallet-session] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-core] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-inherents] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-std] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +### DOC ### + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] diff --git a/pallets/distance/rpc/Cargo.toml b/pallets/distance/rpc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..85baca4ba8c24d9480287dff80b015ffde7b2fa2 --- /dev/null +++ b/pallets/distance/rpc/Cargo.toml @@ -0,0 +1,71 @@ +[package] +authors = ['tuxmain <tuxmain@zettascript.org>'] +description = 'FRAME pallet distance RPC.' +edition = '2018' +homepage = 'https://substrate.dev' +license = 'AGPL-3.0' +name = 'pallet-distance-rpc' +readme = 'README.md' +repository = 'https://git.duniter.org/nodes/rust/duniter-v2s' +version = '1.0.0' + +[dependencies] + +pallet-distance-rpc-runtime-api = { path = "./runtime-api" } + +serde = { version = "1.0", features = ["derive"] } + +# substrate +jsonrpsee = { version = "0.14.0", features = ["server", "macros"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '3.1.5' + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-api] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-blockchain] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-core] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-rpc] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[dependencies.sp-std] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +### DOC ### + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] diff --git a/pallets/distance/rpc/runtime-api/Cargo.toml b/pallets/distance/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..d7f2c41ba2522e5f9cd33328d035391ef41f46ee --- /dev/null +++ b/pallets/distance/rpc/runtime-api/Cargo.toml @@ -0,0 +1,32 @@ +[package] +authors = ['tuxmain <tuxmain@zettascript.org>'] +description = 'FRAME pallet distance RPC runtime API.' +edition = '2018' +homepage = 'https://substrate.dev' +license = 'AGPL-3.0' +name = 'pallet-distance-rpc-runtime-api' +readme = 'README.md' +repository = 'https://git.duniter.org/nodes/rust/duniter-v2s' +version = '1.0.0' + +[dependencies] +pallet-distance = { path = "../../", default-features = false } +sp-api = { git = "https://github.com/duniter/substrate", branch = "duniter-substrate-v0.9.26", default-features = false } +sp-runtime = { git = "https://github.com/duniter/substrate", branch = "duniter-substrate-v0.9.26", default-features = false } + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '3.1.5' + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/duniter/substrate' +branch = 'duniter-substrate-v0.9.26' + +[features] +default = ["std"] +std = [ + "sp-api/std", +] diff --git a/pallets/distance/rpc/runtime-api/src/lib.rs b/pallets/distance/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..94d1576df581e3fb201f5a287169e34ba9f2c71a --- /dev/null +++ b/pallets/distance/rpc/runtime-api/src/lib.rs @@ -0,0 +1,15 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet_distance::{ComputationMetadata, ComputationResult}; + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::*; + +// Here we declare the runtime API. It is implemented it the `impl` block in +// runtime amalgamator file (the `runtime/src/lib.rs`) +sp_api::decl_runtime_apis! { + pub trait DistanceApi<IdtyIndex: Decode + Encode + PartialEq + TypeInfo> { + fn get_computation_metadata() -> Option<ComputationMetadata>; + fn publish_computation_result(computation_result: ComputationResult<IdtyIndex>); + } +} diff --git a/pallets/distance/rpc/src/lib.rs b/pallets/distance/rpc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..56bc4651af5d24955c93feff872a3c350220c755 --- /dev/null +++ b/pallets/distance/rpc/src/lib.rs @@ -0,0 +1,158 @@ +// Copyright 2021 Axiom-Team +// +// This file is part of Substrate-Libre-Currency. +// +// Substrate-Libre-Currency 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. +// +// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. + +use std::sync::Arc; + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::*; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + proc_macros::rpc, + types::error::{CallError, ErrorObject}, +}; +use serde::{Deserialize, Serialize}; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_core::Bytes; +use sp_runtime::{generic::BlockId, traits::Block as BlockT}; + +pub use pallet_distance_rpc_runtime_api::{ComputationResult, DistanceApi as DistanceRuntimeApi}; + +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ComputationMetadata /*<Hash>*/ { + //pub block_hash: Hash, +} + +impl From<pallet_distance_rpc_runtime_api::ComputationMetadata> for ComputationMetadata { + fn from(_val: pallet_distance_rpc_runtime_api::ComputationMetadata) -> Self { + Self {} + } +} + +/*#[derive(Clone, Serialize, Deserialize)] +pub struct ComputationResult { + +} + +impl Into<pallet_distance_rpc_runtime_api::ComputationResult> for ComputationResult { + fn into(self) -> pallet_distance_rpc_runtime_api::ComputationResult { + pallet_distance_rpc_runtime_api::ComputationResult{} + } +}*/ + +#[rpc(client, server)] +pub trait DistanceApi<BlockHash, ResponseType> { + #[method(name = "distance_getComputationMetadata")] + fn get_computation_metadata( + &self, + at: Option<BlockHash>, + ) -> RpcResult<Option<ComputationMetadata>>; + #[method(name = "distance_publishComputationResult")] + fn publish_computation_result( + &self, + encoded_computation_result: Bytes, + at: Option<BlockHash>, + ) -> RpcResult<ResponseType>; +} + +/// Provides RPC methods to query a dispatchable's class, weight and fee. +pub struct Distance<C, P> { + /// Shared reference to the client. + client: Arc<C>, + _marker: std::marker::PhantomData<P>, +} + +impl<C, P> Distance<C, P> { + /// Creates a new instance of the Distance Rpc helper. + pub fn new(client: Arc<C>) -> Self { + Self { + client, + _marker: Default::default(), + } + } +} + +/// Error type of this RPC api. +pub enum Error { + /// The transaction was not decodable. + DecodeError, + /// The call to runtime failed. + RuntimeError, +} + +impl From<Error> for i32 { + fn from(e: Error) -> i32 { + match e { + Error::RuntimeError => 1, + Error::DecodeError => 2, + } + } +} + +#[async_trait] +impl<C, Block, IdtyIndex: 'static + Decode + Encode + PartialEq + Send + Sync + TypeInfo> + DistanceApiServer<<Block as BlockT>::Hash, ()> for Distance<C, (Block, IdtyIndex)> +where + Block: BlockT, + C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + Send + Sync + 'static, + C::Api: DistanceRuntimeApi<Block, IdtyIndex>, +{ + fn get_computation_metadata( + &self, + at: Option<Block::Hash>, + ) -> RpcResult<Option<ComputationMetadata>> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash)); + + api.get_computation_metadata(&at).map_or_else( + |e| { + Err(CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + "Unable to query dispatch info.", + Some(e.to_string()), + )) + .into()) + }, + |v| Ok(v.map(Into::into)), + ) + } + fn publish_computation_result( + &self, + encoded_computation_result: Bytes, + at: Option<Block::Hash>, + ) -> RpcResult<()> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash)); + + let computation_result: ComputationResult<IdtyIndex> = + Decode::decode(&mut &*encoded_computation_result).map_err(|e| { + CallError::Custom(ErrorObject::owned( + Error::DecodeError.into(), + "Unable to query dispatch info.", + Some(format!("{:?}", e)), + )) + })?; + api.publish_computation_result(&at, computation_result) + .map_err(|e| { + CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + "Unable to query dispatch info.", + Some(e.to_string()), + )) + .into() + }) + } +} diff --git a/pallets/distance/src/lib.rs b/pallets/distance/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..f131ceff7f1079b13ec928f560d49515c67986c6 --- /dev/null +++ b/pallets/distance/src/lib.rs @@ -0,0 +1,243 @@ +// Copyright 2021 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_attr(not(feature = "std"), no_std)] + +mod median; +mod types; + +use median::*; +pub use pallet::*; +pub use types::*; + +use frame_support::traits::StorageVersion; +use pallet_authority_members::SessionIndex; +use sp_inherents::{InherentData, InherentIdentifier}; +use sp_std::convert::TryInto; + +pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"distanc0"; + +type IdtyIndex = u32; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_runtime::Perbill; + + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] + #[pallet::without_storage_info] + pub struct Pallet<T, I = ()>(PhantomData<(T, I)>); + #[pallet::config] + pub trait Config<I: 'static = ()>: + frame_system::Config + + pallet_certification::Config<I, IdtyIndex = IdtyIndex> + + pallet_identity::Config<IdtyIndex = IdtyIndex> + + pallet_session::Config + { + /// Number of sessions before starting a new computation + type FinalityLag: Get<u32>; + /// Duration of a computation (in sessions) + type ComputationMaxDelay: Get<u32>; + /// Maximum number of identities in the evaluation queue + type QueueSize: Get<Option<u32>>; + /// Maximum number of identities to be evaluated in a session + type MaxEvaluationsPerSession: Get<u32>; + /// Minimum ratio of accessible referees + type MinAccessibleReferees: Get<Perbill>; + } + + // STORAGE // + + /// Identities to be evaluated in next sessions + #[pallet::storage] + pub type EvaluationQueue<T: Config<I>, I: 'static = ()> = CountedStorageMap< + _, + Twox64Concat, + <T as pallet_certification::Config<I>>::IdtyIndex, + (), + OptionQuery, + GetDefault, + <T as Config<I>>::QueueSize, + >; + + /*#[pallet::storage] + pub type IdentitiesBeingEvaluated<T: Config<I>, I: 'static = ()> = + StorageValue<_, Vec<<T as pallet_certification::Config<I>>::IdtyIndex>, OptionQuery>;*/ + + /// Number of evaluators coming up with some value. + /// Indexed by identity index, then by value. + #[pallet::storage] + pub type EvaluatedIdentities<T: Config<I>, I: 'static = ()> = StorageMap< + _, + Twox64Concat, + <T as pallet_certification::Config<I>>::IdtyIndex, + MedianAcc<Perbill>, + OptionQuery, + GetDefault, + >; + + /*/// Number of evaluators coming up with some referee count, indexed by referee count. + #[pallet::storage] + pub type Referees<T: Config<I>, I: 'static = ()> = StorageValue<_, MedianAcc<u32>, OptionQuery>;*/ + + #[pallet::error] + pub enum Error<T, I = ()> { + IdentityNotInEvaluation, + IdtyNotFound, + NonEligibleForEvaluation, + QueueFull, + UnauthorizedIdentity, + } + + #[pallet::hooks] + impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {} + + // CALLS // + + #[pallet::call] + impl<T: Config<I>, I: 'static> Pallet<T, I> { + #[pallet::weight(1_000_000_000)] + pub fn evaluate_distance( + origin: OriginFor<T>, + idty_index: <T as pallet_certification::Config<I>>::IdtyIndex, + ) -> DispatchResultWithPostInfo { + let _who = ensure_signed(origin)?; + // TODO check whether origin is authorized to request evaluation + // TODO (maybe) check whether identity is already in queue + // TODO sort queue chronologically + + if let Some(queue_size) = <T as Config<I>>::QueueSize::get() { + ensure!( + EvaluationQueue::<T, I>::count() < queue_size, + Error::<T, I>::QueueFull + ); + } + + ensure!( + pallet_identity::Identities::<T>::contains_key(idty_index), + Error::<T, I>::IdtyNotFound + ); + + EvaluationQueue::<T, I>::insert(idty_index, ()); + + Ok(().into()) + } + + #[pallet::weight(1_000_000_000)] + pub fn update_evaluation( + origin: OriginFor<T>, + computation_result: ComputationResult<<T as pallet_identity::Config>::IdtyIndex>, + ) -> DispatchResult { + ensure_none(origin)?; + //assert!(!DidUpdate::<T>::exists(), "At most one distance computation result can be submitted per block"); + + for (identity, result) in computation_result.identities { + EvaluatedIdentities::<T, I>::try_mutate::<_, _, Error<T, I>, _>( + identity, + |median_acc| { + median_acc + .as_mut() + .ok_or(Error::<T, I>::IdentityNotInEvaluation)? + .push(result); + Ok(()) + }, + )?; + } + + Ok(()) + } + } + + // PUBLIC FUNCTIONS // + + impl<T: Config<I>, I: 'static> Pallet<T, I> { + pub fn get_computation_metadata( + ) -> Option<ComputationMetadata /*<<T as frame_system::Config>::Hash>*/> { + None + } + pub fn publish_computation_result( + _computation_result: ComputationResult<<T as pallet_identity::Config>::IdtyIndex>, + ) { + } + } + + // INTERNAL FUNCTIONS // + + impl<T: Config<I>, I: 'static> Pallet<T, I> {} + + impl<T: Config<I>, I: 'static> pallet_authority_members::OnNewSession for Pallet<T, I> { + fn on_new_session(index: SessionIndex) -> Weight { + match index % 3 { + 0 => { + EvaluationQueue::<T, I>::drain() + .zip(0..<T as Config<I>>::MaxEvaluationsPerSession::get()) + .map(|((identity, _), _)| identity) + .for_each(|identity| { + EvaluatedIdentities::<T, I>::insert(identity, MedianAcc::new()) + }); + } + 1 => {} + 2 => { + for (_identity, median_acc) in EvaluatedIdentities::<T, I>::drain() { + if let Some(median_result) = median_acc.get_median() { + let median = match median_result { + MedianResult::One(m) => m, + MedianResult::Two(m1, m2) => m1 + (m2 - m1) / 2, // Avoid overflow (since max is 1) + }; + if median >= T::MinAccessibleReferees::get() { + todo!(); + } else { + todo!(); + } + } + } + } + _ => unreachable!("index % 3 < 3"), + } + 0 + } + } + + #[pallet::inherent] + impl<T: Config<I>, I: 'static> ProvideInherent for Pallet<T, I> { + type Call = Call<T, I>; + type Error = InherentError; + + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn create_inherent(data: &InherentData) -> Option<Self::Call> { + let inherent_data = data + .get_data::<ComputationResult<<T as pallet_identity::Config>::IdtyIndex>>( + &INHERENT_IDENTIFIER, + ) + .expect("Distance inherent data not correctly encoded") + .expect("Distance inherent data must be provided"); + Some(Call::update_evaluation { + computation_result: inherent_data, + }) + } + fn is_inherent(_call: &Self::Call) -> bool { + true + } + } +} diff --git a/pallets/distance/src/median.rs b/pallets/distance/src/median.rs new file mode 100644 index 0000000000000000000000000000000000000000..5d08c656ad88c637268b172f5d9912a9830440d1 --- /dev/null +++ b/pallets/distance/src/median.rs @@ -0,0 +1,143 @@ +// Copyright 2021 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 frame_support::pallet_prelude::*; +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)>, + median_index: Option<u32>, + median_subindex: u32, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum MedianResult<T: Clone + Ord> { + One(T), + Two(T, T), +} + +impl<T: Clone + Decode + Encode + Ord + TypeInfo> MedianAcc<T> { + pub fn new() -> Self { + Self { + samples: Vec::new(), + median_index: None, + median_subindex: 0, + } + } + + pub fn push(&mut self, sample: T) { + if let Some(median_index) = &mut self.median_index { + match self + .samples + .binary_search_by_key(&sample, |(s, _n)| s.clone()) + { + Ok(sample_index) => { + self.samples.get_mut(sample_index).expect("unreachable").1 += 1; + match (sample_index as u32).cmp(median_index) { + Ordering::Greater => { + if self.median_subindex + == self + .samples + .get(*median_index as usize) + .expect("unreachable") + .1 + * 2 + - 1 + { + self.median_subindex = 0; + *median_index += 1; + } else { + self.median_subindex += 1; + } + } + Ordering::Equal => { + self.median_subindex += 1; + } + Ordering::Less => { + if self.median_subindex == 0 { + *median_index -= 1; + self.median_subindex = self + .samples + .get(*median_index as usize) + .expect("unreachable") + .1 + * 2 + - 1; + } else { + self.median_subindex -= 1; + } + } + } + } + Err(sample_index) => { + self.samples.insert(sample_index, (sample, 1)); + if *median_index as usize >= sample_index { + if self.median_subindex == 0 { + self.median_subindex = self + .samples + .get(*median_index as usize) + .expect("unreachable") + .1 + * 2 + - 1; + } else { + self.median_subindex -= 1; + *median_index += 1; + } + } else if self.median_subindex + == self + .samples + .get(*median_index as usize) + .expect("unreachable") + .1 + * 2 + - 1 + { + self.median_subindex = 0; + *median_index += 1; + } else { + self.median_subindex += 1; + } + } + } + } else { + self.samples.push((sample, 1)); + self.median_index = Some(0); + } + } + + pub fn get_median(&self) -> Option<MedianResult<T>> { + self.median_index.map(|median_index| { + let (median_sample, median_n) = self + .samples + .get(median_index as usize) + .expect("unreachable"); + if self.median_subindex == median_n * 2 - 1 { + MedianResult::Two( + median_sample.clone(), + self.samples + .get(median_index as usize + 1) + .expect("unreachable") + .0 + .clone(), + ) + } else { + MedianResult::One(median_sample.clone()) + } + }) + } +} diff --git a/pallets/distance/src/types.rs b/pallets/distance/src/types.rs new file mode 100644 index 0000000000000000000000000000000000000000..9a39d09f80d5b20773ac9244d077a9802d001dbb --- /dev/null +++ b/pallets/distance/src/types.rs @@ -0,0 +1,42 @@ +// Copyright 2021 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 codec::{Decode, Encode}; +use frame_support::pallet_prelude::*; +use sp_inherents::IsFatalError; +use sp_runtime::Perbill; +use sp_std::vec::Vec; + +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ComputationMetadata /*<Hash>*/ { + //pub block_hash: Hash, +} + +#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ComputationResult<IdtyIndex: Decode + Encode + PartialEq + TypeInfo> { + pub identities: Vec<(IdtyIndex, Perbill)>, +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub enum InherentError { + Foo, +} + +impl IsFatalError for InherentError { + fn is_fatal_error(&self) -> bool { + false + } +} diff --git a/resources/metadata.scale b/resources/metadata.scale index ff4c1c40a4d58bf8b3d8dc74373830583ef1ddbb..11bade8ba693169b83e825b6eb0d36a987a4a3dd 100644 Binary files a/resources/metadata.scale and b/resources/metadata.scale differ diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index a4685b21bdeb8eefd696b83106d3ad4beb53afd9..7eb520c2e682083dcae1497121a26939b41d87d8 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -33,6 +33,8 @@ std = [ 'pallet-babe/std', 'pallet-balances/std', 'pallet-certification/std', + 'pallet-distance/std', + 'pallet-distance-rpc-runtime-api/std', 'pallet-duniter-account/std', 'pallet-duniter-wot/std', 'pallet-grandpa/std', @@ -65,6 +67,8 @@ try-runtime = [ duniter-primitives = { path = '../../primitives/duniter', default-features = false } pallet-authority-members = { path = '../../pallets/authority-members', default-features = false } pallet-certification = { path = '../../pallets/certification', default-features = false } +pallet-distance = { path = "../../pallets/distance", default-features = false } +pallet-distance-rpc-runtime-api = { path = "../../pallets/distance/rpc/runtime-api", default-features = false } pallet-duniter-account = { path = '../../pallets/duniter-account', default-features = false } pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false } pallet-identity = { path = '../../pallets/identity', default-features = false } diff --git a/runtime/common/src/apis.rs b/runtime/common/src/apis.rs index dcd38e6e10ea4dccbea315532a2763c162757144..b02a4b61b3aeb47bff670de0de6b255c4f82f292 100644 --- a/runtime/common/src/apis.rs +++ b/runtime/common/src/apis.rs @@ -208,6 +208,15 @@ macro_rules! runtime_apis { TransactionPayment::query_fee_details(uxt, len) } } + impl pallet_distance_rpc_runtime_api::DistanceApi<Block, u32> for Runtime { + fn get_computation_metadata() -> Option<pallet_distance_rpc_runtime_api::ComputationMetadata/*<<T as frame_system::Config>::Hash>*/> + { + Distance::get_computation_metadata() + } + fn publish_computation_result(computation_result: pallet_distance_rpc_runtime_api::ComputationResult<u32>) { + Distance::publish_computation_result(computation_result) + } + } #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime<Block> for Runtime { diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs index e09b1d30fd008f604e4834e9f4b8a0c3531be31a..edf25c2943f4c2037572e45cd95c1334bc535f08 100644 --- a/runtime/common/src/handlers.rs +++ b/runtime/common/src/handlers.rs @@ -27,11 +27,11 @@ use sp_runtime::traits::IsMember; pub struct OnNewSessionHandler<Runtime>(core::marker::PhantomData<Runtime>); impl<Runtime> pallet_authority_members::traits::OnNewSession for OnNewSessionHandler<Runtime> where - Runtime: pallet_provide_randomness::Config, + Runtime: pallet_provide_randomness::Config + pallet_distance::Config<Instance1>, { - fn on_new_session(_index: sp_staking::SessionIndex) -> Weight { + fn on_new_session(index: sp_staking::SessionIndex) -> Weight { pallet_provide_randomness::Pallet::<Runtime>::on_new_epoch(); - 0 + pallet_distance::Pallet::<Runtime, Instance1>::on_new_session(index) } } diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index cdcaa4944cda9a111335cbb204f901155570c1dc..ca02e9b612473c542da6ae72084caa6cfd01958c 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -468,6 +468,17 @@ macro_rules! pallets_config { type OnRemovedCert = Wot; type ValidityPeriod = ValidityPeriod; } + parameter_types! { + pub const MinAccessibleReferees: Perbill = Perbill::from_percent(80); + } + impl pallet_distance::Config<Instance1> for Runtime { + //type IdtyIndex: IdtyIndex; + type FinalityLag = frame_support::traits::ConstU32<1>; + type ComputationMaxDelay = frame_support::traits::ConstU32<1>; + type QueueSize = frame_support::traits::ConstU32<800>; + type MaxEvaluationsPerSession = frame_support::traits::ConstU32<100>; + type MinAccessibleReferees = MinAccessibleReferees; + } // SMITHSÂ SUB-WOT // diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml index 65d3ec9afbf49d90c96ddf69b9189a25d293207c..8ec993dbacf4f2ba4f1fe1ab81007bec2509132d 100644 --- a/runtime/gdev/Cargo.toml +++ b/runtime/gdev/Cargo.toml @@ -63,6 +63,8 @@ std = [ 'pallet-balances/std', 'pallet-certification/std', 'pallet-collective/std', + 'pallet-distance/std', + 'pallet-distance-rpc-runtime-api/std', 'pallet-duniter-test-parameters/std', 'pallet-duniter-account/std', 'pallet-duniter-wot/std', @@ -136,6 +138,8 @@ sp-keyring = { git = 'https://github.com/duniter/substrate', branch = 'duniter-s common-runtime = { path = "../common", default-features = false } pallet-authority-members = { path = '../../pallets/authority-members', default-features = false } pallet-certification = { path = '../../pallets/certification', default-features = false } +pallet-distance = { path = "../../pallets/distance", default-features = false } +pallet-distance-rpc-runtime-api = { path = "../../pallets/distance/rpc/runtime-api", default-features = false } pallet-duniter-test-parameters = { path = '../../pallets/duniter-test-parameters', default-features = false } pallet-duniter-account = { path = '../../pallets/duniter-account', default-features = false } pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = false } diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs index eec2dc9c2bb696cc5e0b5f97f8e212fef237bd49..994f1b2b0044e01937876ec1a679b56b2ddceb54 100644 --- a/runtime/gdev/src/lib.rs +++ b/runtime/gdev/src/lib.rs @@ -313,6 +313,7 @@ construct_runtime!( Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41, Membership: pallet_membership::<Instance1>::{Pallet, Call, Config<T>, Storage, Event<T>} = 42, Cert: pallet_certification::<Instance1>::{Pallet, Call, Config<T>, Storage, Event<T>} = 43, + Distance: pallet_distance::<Instance1>::{Call, Storage} = 44, // Smiths Sub-Wot SmithsSubWot: pallet_duniter_wot::<Instance2>::{Pallet} = 50,