Skip to content

Commit

Permalink
Merge solochain's profile follows pallet to parachain's profiles (#111)
Browse files Browse the repository at this point in the history
* WIP: move profile follows pallet from solochain

* Update `profile-follows` to the new proc macro

* Rename profile-follows to account-follows

Add account-follows to runtime
  • Loading branch information
F3Joule committed Jul 26, 2022
1 parent 214c985 commit 1018649
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 6 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions pallets/account-follows/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = 'pallet-account-follows'
version = '0.1.7'
authors = ['DappForce <dappforce@pm.me>']
edition = '2021'
license = 'GPL-3.0-only'
homepage = 'https://subsocial.network'
repository = 'https://github.com/dappforce/subsocial-parachain'
description = 'Pallet to follow/unfollow accounts'
keywords = ['blockchain', 'cryptocurrency', 'social-network', 'news-feed', 'marketplace']
categories = ['cryptography::cryptocurrencies']

[features]
default = ['std']
std = [
'codec/std',
'scale-info/std',
'frame-support/std',
'frame-system/std',
'sp-std/std',
'pallet-profiles/std',
'subsocial-support/std',
]

[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }

# Local dependencies
pallet-profiles = { default-features = false, path = '../profiles' }
subsocial-support = { default-features = false, path = '../support' }

# Substrate dependencies
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-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.24', default-features = false }
48 changes: 48 additions & 0 deletions pallets/account-follows/rpc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[package]
name = 'profile-follows-rpc'
version = '0.7.3'
authors = ['DappForce <dappforce@pm.me>']
edition = '2018'
license = 'GPL-3.0-only'
homepage = 'https://subsocial.network'
repository = 'https://github.com/dappforce/dappforce-subsocial-node'
description = 'RPC methods for the profile-follows pallet'
keywords = ['blockchain', 'cryptocurrency', 'social-network', 'news-feed', 'marketplace']
categories = ['cryptography::cryptocurrencies']

[dependencies.serde]
optional = true
features = ['derive']
version = '1.0.119'

[dependencies.codec]
default-features = false
features = ['derive']
package = 'parity-scale-codec'
version = '2.0.0'

[dependencies]
jsonrpc-core = '18.0.0'
jsonrpc-core-client = '18.0.0'
jsonrpc-derive = '18.0.0'

# Local dependencies
pallet-utils = { default-features = false, path = '../../utils' }

# Custom Runtime API
profile-follows-runtime-api = { default-features = false, path = 'runtime-api' }

# Substrate dependencies
sp-api = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }
sp-blockchain = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }
sp-rpc = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }
sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }

[features]
default = ['std']
std = [
'serde',
'sp-runtime/std',
'sp-api/std',
'profile-follows-runtime-api/std',
]
37 changes: 37 additions & 0 deletions pallets/account-follows/rpc/runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = 'profile-follows-runtime-api'
version = '0.7.3'
authors = ['DappForce <dappforce@pm.me>']
edition = '2018'
license = 'GPL-3.0-only'
homepage = 'https://subsocial.network'
repository = 'https://github.com/dappforce/dappforce-subsocial-node'
description = 'Runtime API definition for the profile-follows pallet'
keywords = ['blockchain', 'cryptocurrency', 'social-network', 'news-feed', 'marketplace']
categories = ['cryptography::cryptocurrencies']

[dependencies.serde]
optional = true
features = ["derive"]
version = "1.0.119"

[dependencies.codec]
default-features = false
features = ['derive']
package = 'parity-scale-codec'
version = '2.0.0'

[dependencies]
# Substrate dependencies
sp-api = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }
sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }
sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.12', default-features = false }

[features]
default = ['std']
std = [
'serde',
'sp-api/std',
'sp-std/std',
'sp-runtime/std',
]
12 changes: 12 additions & 0 deletions pallets/account-follows/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![cfg_attr(not(feature = "std"), no_std)]

use codec::Codec;
use sp_std::vec::Vec;

sp_api::decl_runtime_apis! {
pub trait ProfileFollowsApi<AccountId> where
AccountId: Codec
{
fn filter_followed_accounts(account: AccountId, maybe_following: Vec<AccountId>) -> Vec<AccountId>;
}
}
57 changes: 57 additions & 0 deletions pallets/account-follows/rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::sync::Arc;
use codec::Codec;
use sp_blockchain::HeaderBackend;
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
use jsonrpc_core::Result;
use jsonrpc_derive::rpc;
use sp_api::ProvideRuntimeApi;

use pallet_utils::rpc::map_rpc_error;
pub use profile_follows_runtime_api::ProfileFollowsApi as ProfileFollowsRuntimeApi;

#[rpc]
pub trait ProfileFollowsApi<BlockHash, AccountId> {
#[rpc(name = "profileFollows_filterFollowedAccounts")]
fn filter_followed_accounts(
&self,
at: Option<BlockHash>,
account: AccountId,
maybe_following: Vec<AccountId>,
) -> Result<Vec<AccountId>>;
}

pub struct ProfileFollows<C, M> {
client: Arc<C>,
_marker: std::marker::PhantomData<M>,
}

impl<C, M> ProfileFollows<C, M> {
pub fn new(client: Arc<C>) -> Self {
Self {
client,
_marker: Default::default(),
}
}
}

impl<C, Block, AccountId> ProfileFollowsApi<<Block as BlockT>::Hash, AccountId>
for ProfileFollows<C, Block>
where
Block: BlockT,
AccountId: Codec,
C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
C::Api: ProfileFollowsRuntimeApi<Block, AccountId>,
{
fn filter_followed_accounts(
&self, at:
Option<<Block as BlockT>::Hash>,
account: AccountId,
maybe_following: Vec<AccountId>,
) -> Result<Vec<AccountId>> {
let api = self.client.runtime_api();
let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));

let runtime_api_result = api.filter_followed_accounts(&at, account, maybe_following);
runtime_api_result.map_err(map_rpc_error)
}
}
111 changes: 111 additions & 0 deletions pallets/account-follows/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

// pub mod rpc;

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

use subsocial_support::remove_from_vec;

/// The pallet's configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}

#[pallet::pallet]
#[pallet::generate_store(pub (super) trait Store)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);

#[pallet::storage]
#[pallet::getter(fn account_followers)]
pub(super) type AccountFollowers<T: Config> =
StorageMap<_, Twox64Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn account_followed_by_account)]
pub(super) type AccountFollowedByAccount<T: Config> =
StorageMap<_, Blake2_128Concat, (T::AccountId, T::AccountId), bool, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn accounts_followed_by_account)]
pub(super) type AccountsFollowedByAccount<T: Config> =
StorageMap<_, Twox64Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
AccountFollowed { follower: T::AccountId, account: T::AccountId },
AccountUnfollowed { follower: T::AccountId, account: T::AccountId },
}

#[pallet::error]
pub enum Error<T> {
/// Follower social account was not found by id.
FollowerAccountNotFound,
/// Social account that is being followed was not found by id.
FollowedAccountNotFound,

/// Account can not follow itself.
AccountCannotFollowItself,
/// Account can not unfollow itself.
AccountCannotUnfollowItself,

/// Account (Alice) is already a follower of another account (Bob).
AlreadyAccountFollower,
/// Account (Alice) is not a follower of another account (Bob).
NotAccountFollower,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(2, 3))]
pub fn follow_account(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
let follower = ensure_signed(origin)?;

ensure!(follower != account, Error::<T>::AccountCannotFollowItself);
ensure!(
!<AccountFollowedByAccount<T>>::contains_key((follower.clone(), account.clone())),
Error::<T>::AlreadyAccountFollower
);

AccountsFollowedByAccount::<T>::mutate(follower.clone(), |ids| {
ids.push(account.clone())
});
AccountFollowers::<T>::mutate(account.clone(), |ids| ids.push(follower.clone()));
AccountFollowedByAccount::<T>::insert((follower.clone(), account.clone()), true);

Self::deposit_event(Event::AccountFollowed { follower, account });
Ok(())
}

#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(2, 3))]
pub fn unfollow_account(origin: OriginFor<T>, account: T::AccountId) -> DispatchResult {
let follower = ensure_signed(origin)?;

ensure!(follower != account, Error::<T>::AccountCannotUnfollowItself);
ensure!(
<AccountFollowedByAccount<T>>::contains_key((follower.clone(), account.clone())),
Error::<T>::NotAccountFollower
);

AccountsFollowedByAccount::<T>::mutate(follower.clone(), |account_ids| {
remove_from_vec(account_ids, account.clone())
});
AccountFollowers::<T>::mutate(account.clone(), |account_ids| {
remove_from_vec(account_ids, follower.clone())
});
AccountFollowedByAccount::<T>::remove((follower.clone(), account.clone()));

Self::deposit_event(Event::AccountUnfollowed { follower, account });
Ok(())
}
}
}
11 changes: 11 additions & 0 deletions pallets/account-follows/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use sp_std::prelude::*;

use crate::{Module, Config};

impl<T: Config> Module<T> {
pub fn filter_followed_accounts(account: T::AccountId, maybe_following: Vec<T::AccountId>) -> Vec<T::AccountId> {
maybe_following.iter()
.filter(|maybe_following| Self::account_followed_by_account((&account, maybe_following)))
.cloned().collect()
}
}
2 changes: 2 additions & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ serde = { version = "1.0.137", optional = true, features = ["derive"] }
smallvec = "1.6.1"

# Local
pallet-account-follows = { path = "../pallets/account-follows", default-features = false }
pallet-domains = { path = "../pallets/domains", default-features = false }
pallet-permissions = { path = '../pallets/permissions', default-features = false }
pallet-posts = { path = '../pallets/posts', default-features = false }
Expand Down Expand Up @@ -142,6 +143,7 @@ std = [
"pallet-vesting/std",
"pallet-utility/std",
"pallet-randomness-collective-flip/std",
"pallet-account-follows/std",
"pallet-domains/std",
"pallet-permissions/std",
"pallet-posts/std",
Expand Down
17 changes: 11 additions & 6 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,10 @@ impl pallet_space_ownership::Config for Runtime {
type ProfileManager = Profiles;
}

impl pallet_account_follows::Config for Runtime {
type Event = Event;
}

// Create the runtime by composing the FRAME pallets that were previously configured.
construct_runtime!(
pub enum Runtime where
Expand Down Expand Up @@ -628,12 +632,13 @@ construct_runtime!(

Permissions: pallet_permissions = 70,
Roles: pallet_roles = 71,
Profiles: pallet_profiles = 72,
SpaceFollows: pallet_space_follows = 73,
SpaceOwnership: pallet_space_ownership = 74,
Spaces: pallet_spaces = 75,
Posts: pallet_posts = 76,
Reactions: pallet_reactions = 77,
AccountFollows: pallet_account_follows = 72,
Profiles: pallet_profiles = 73,
SpaceFollows: pallet_space_follows = 74,
SpaceOwnership: pallet_space_ownership = 75,
Spaces: pallet_spaces = 76,
Posts: pallet_posts = 77,
Reactions: pallet_reactions = 78,

// Temporary
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>} = 255,
Expand Down

0 comments on commit 1018649

Please sign in to comment.