diff --git a/Cargo.lock b/Cargo.lock index bb090e51..6b5a8b3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5076,11 +5076,16 @@ dependencies = [ name = "pallet-account-follows" version = "0.1.7" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-profiles", "parity-scale-codec", "scale-info", + "smallvec", + "sp-core", + "sp-io", + "sp-runtime", "sp-std", "subsocial-support", ] diff --git a/pallets/account-follows/Cargo.toml b/pallets/account-follows/Cargo.toml index 99a26465..11fd5ac4 100644 --- a/pallets/account-follows/Cargo.toml +++ b/pallets/account-follows/Cargo.toml @@ -10,17 +10,26 @@ description = 'Pallet to follow/unfollow accounts' keywords = ['blockchain', 'cryptocurrency', 'social-network', 'news-feed', 'marketplace'] categories = ['cryptography::cryptocurrencies'] +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + [features] default = ['std'] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] std = [ 'codec/std', 'scale-info/std', + "frame-benchmarking/std", 'frame-support/std', 'frame-system/std', 'sp-std/std', 'pallet-profiles/std', 'subsocial-support/std', ] +try-runtime = ["frame-support/try-runtime"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } @@ -31,6 +40,13 @@ pallet-profiles = { default-features = false, path = '../profiles' } subsocial-support = { default-features = false, path = '../support' } # Substrate dependencies +frame-benchmarking = { optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false } frame-support = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.24', default-features = false } frame-system = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.24', default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false } sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.24', default-features = false } + +[dev-dependencies] +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false } +smallvec = "1.6.1" \ No newline at end of file diff --git a/pallets/account-follows/src/benchmarking.rs b/pallets/account-follows/src/benchmarking.rs new file mode 100644 index 00000000..fbd1c574 --- /dev/null +++ b/pallets/account-follows/src/benchmarking.rs @@ -0,0 +1,66 @@ +#![cfg(feature = "runtime-benchmarks")] + +use frame_benchmarking::{account, benchmarks}; +use frame_support::ensure; +use frame_system::RawOrigin; + +use crate as pallet_account_follows; + +use super::*; + +benchmarks! { + follow_account { + let follower: T::AccountId = account("follower", 0, 0); + let followee: T::AccountId = account("followee", 1, 1); + + let follower_origin = RawOrigin::Signed(follower.clone()); + + }: _(follower_origin, followee.clone()) + verify { + ensure!( + AccountsFollowedByAccount::::get(follower.clone()).contains(&followee), + "AccountsFollowedByAccount didn't get updated", + ); + ensure!( + AccountFollowers::::get(followee.clone()).contains(&follower), + "AccountFollowers didn't get updated", + ); + ensure!( + AccountFollowedByAccount::::get((follower.clone(), followee.clone())), + "AccountFollowedByAccount didn't get updated", + ); + } + + unfollow_account { + let follower: T::AccountId = account("follower", 0, 0); + let followee: T::AccountId = account("followee", 1, 1); + + let follower_origin = RawOrigin::Signed(follower.clone()); + + ensure!(pallet_account_follows::Pallet::::follow_account( + follower_origin.clone().into(), + followee.clone(), + ).is_ok(), "follow_account call returned an err"); + + }: _(follower_origin, followee.clone()) + verify { + ensure!( + !AccountsFollowedByAccount::::get(follower.clone()).contains(&followee), + "AccountsFollowedByAccount didn't get updated", + ); + ensure!( + !AccountFollowers::::get(followee.clone()).contains(&follower), + "AccountFollowers didn't get updated", + ); + ensure!( + !AccountFollowedByAccount::::get((follower.clone(), followee.clone())), + "AccountFollowedByAccount didn't get updated", + ); + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::ExtBuilder::build(), + crate::mock::TestRuntime, + ); +} diff --git a/pallets/account-follows/src/lib.rs b/pallets/account-follows/src/lib.rs index f33cc414..42e4e010 100644 --- a/pallets/account-follows/src/lib.rs +++ b/pallets/account-follows/src/lib.rs @@ -1,12 +1,24 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + pub use pallet::*; +pub mod weights; + // pub mod rpc; #[frame_support::pallet] pub mod pallet { use super::*; + use crate::weights::WeightInfo; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -19,6 +31,9 @@ pub mod pallet { pub trait Config: frame_system::Config { /// The overarching event type. type Event: From> + IsType<::Event>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -68,7 +83,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - #[pallet::weight(1_250_000 + T::DbWeight::get().reads_writes(2, 3))] + #[pallet::weight(< T as Config >::WeightInfo::follow_account())] pub fn follow_account(origin: OriginFor, account: T::AccountId) -> DispatchResult { let follower = ensure_signed(origin)?; @@ -88,7 +103,7 @@ pub mod pallet { Ok(()) } - #[pallet::weight(1_250_000 + T::DbWeight::get().reads_writes(2, 3))] + #[pallet::weight(< T as Config >::WeightInfo::unfollow_account())] pub fn unfollow_account(origin: OriginFor, account: T::AccountId) -> DispatchResult { let follower = ensure_signed(origin)?; diff --git a/pallets/account-follows/src/mock.rs b/pallets/account-follows/src/mock.rs new file mode 100644 index 00000000..793cc4ac --- /dev/null +++ b/pallets/account-follows/src/mock.rs @@ -0,0 +1,73 @@ +use frame_support::traits::{ConstU32, ConstU64}; +use sp_core::H256; +use sp_io::TestExternalities; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +use crate as pallet_account_follows; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; +pub(super) type AccountId = u64; +type BlockNumber = u64; + +frame_support::construct_runtime!( + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + AccountFollows: pallet_account_follows, + } +); + +impl frame_system::Config for TestRuntime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_account_follows::Config for TestRuntime { + type Event = Event; + type WeightInfo = (); +} + +pub struct ExtBuilder; + +impl ExtBuilder { + pub(crate) fn build() -> TestExternalities { + let storage = + &mut frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let mut ext = TestExternalities::from(storage.clone()); + ext.execute_with(|| { + System::set_block_number(1); + }); + + ext + } +} diff --git a/pallets/account-follows/src/tests.rs b/pallets/account-follows/src/tests.rs new file mode 100644 index 00000000..838ea0c6 --- /dev/null +++ b/pallets/account-follows/src/tests.rs @@ -0,0 +1,95 @@ +use frame_support::{assert_noop, assert_ok}; + +use crate::{mock::*, pallet::*, Error, Event}; + +#[test] +fn follow_account_should_fail_if_account_tries_to_follow_himself() { + ExtBuilder::build().execute_with(|| { + let account = 1; + assert_noop!( + AccountFollows::follow_account(Origin::signed(account), account), + Error::::AccountCannotFollowItself, + ); + }); +} + +#[test] +fn follow_account_should_fail_if_already_a_follower() { + ExtBuilder::build().execute_with(|| { + let follower = 1; + let followee = 2; + + assert_ok!(AccountFollows::follow_account(Origin::signed(follower), followee)); + + assert_noop!( + AccountFollows::follow_account(Origin::signed(follower), followee), + Error::::AlreadyAccountFollower, + ); + }); +} + +#[test] +fn follow_account_should_work() { + ExtBuilder::build().execute_with(|| { + let follower = 1; + let followee = 2; + + assert_ok!(AccountFollows::follow_account(Origin::signed(follower), followee)); + + assert!(AccountsFollowedByAccount::::get(follower.clone()).contains(&followee)); + assert!(AccountFollowers::::get(followee.clone()).contains(&follower)); + assert!(AccountFollowedByAccount::::get((follower.clone(), followee.clone()))); + + System::assert_last_event(Event::AccountFollowed { + follower, + account: followee, + }.into()); + }); +} + +#[test] +fn unfollow_account_should_fail_if_account_tries_to_unfollow_himself() { + ExtBuilder::build().execute_with(|| { + let account = 1; + assert_noop!( + AccountFollows::unfollow_account(Origin::signed(account), account), + Error::::AccountCannotUnfollowItself, + ); + }); +} + +#[test] +fn unfollow_account_should_fail_if_account_not_a_follower() { + ExtBuilder::build().execute_with(|| { + let follower = 1; + let followee = 2; + + assert_noop!( + AccountFollows::unfollow_account(Origin::signed(follower), followee), + Error::::NotAccountFollower, + ); + }); +} + +#[test] +fn unfollow_account_should_work() { + ExtBuilder::build().execute_with(|| { + let follower = 1; + let followee = 2; + + assert_ok!(AccountFollows::follow_account(Origin::signed(follower), followee)); + + assert_ok!(AccountFollows::unfollow_account(Origin::signed(follower), followee)); + + assert!( + !AccountsFollowedByAccount::::get(follower.clone()).contains(&followee) + ); + assert!(!AccountFollowers::::get(followee.clone()).contains(&follower)); + assert!(!AccountFollowedByAccount::::get(( + follower.clone(), + followee.clone() + ))); + + System::assert_last_event(Event::AccountUnfollowed { follower, account: followee }.into()); + }); +} diff --git a/pallets/account-follows/src/weights.rs b/pallets/account-follows/src/weights.rs new file mode 100644 index 00000000..d148f8cb --- /dev/null +++ b/pallets/account-follows/src/weights.rs @@ -0,0 +1,85 @@ +//! Autogenerated weights for pallet_account_follows +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-17, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: + // ./scripts/../target/release/subsocial-collator + // benchmark + // pallet + // --chain + // dev + // --execution + // wasm + // --wasm-execution + // Compiled + // --pallet + // pallet_account_follows + // --extrinsic + // * + // --steps + // 50 + // --repeat + // 20 + // --heap-pages + // 4096 + // --output + // ./pallets/account-follows/src/weights.rs + // --template + // ./.maintain/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(non_snake_case)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_account_follows. +pub trait WeightInfo { + fn follow_account() -> Weight; + fn unfollow_account() -> Weight; +} + +/// Weights for pallet_account_follows using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); + impl WeightInfo for SubstrateWeight { + // Storage: AccountFollows AccountFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountsFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountFollowers (r:1 w:1) + fn follow_account() -> Weight { + (15_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + // Storage: AccountFollows AccountFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountsFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountFollowers (r:1 w:1) + fn unfollow_account() -> Weight { + (19_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + } + + // For backwards compatibility and tests + impl WeightInfo for () { + // Storage: AccountFollows AccountFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountsFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountFollowers (r:1 w:1) + fn follow_account() -> Weight { + (15_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + // Storage: AccountFollows AccountFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountsFollowedByAccount (r:1 w:1) + // Storage: AccountFollows AccountFollowers (r:1 w:1) + fn unfollow_account() -> Weight { + (19_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 88245171..ab9aade2 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -177,6 +177,7 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "pallet-account-follows/runtime-benchmarks", "pallet-domains/runtime-benchmarks", "pallet-energy/runtime-benchmarks", "pallet-reactions/runtime-benchmarks", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c8bc384f..35a1af62 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -702,6 +702,7 @@ impl pallet_space_ownership::Config for Runtime { impl pallet_account_follows::Config for Runtime { type Event = Event; + type WeightInfo = pallet_account_follows::weights::SubstrateWeight; } @@ -791,6 +792,7 @@ mod benches { [pallet_proxy, Proxy] [pallet_utility, Utility] [pallet_collator_selection, CollatorSelection] + [pallet_account_follows, AccountFollows] [pallet_domains, Domains] [pallet_energy, Energy] [pallet_profiles, Profiles]