lib.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. // Ensure we're `no_std` when compiling for Wasm.
  2. #![cfg_attr(not(feature = "std"), no_std)]
  3. // Clippy linter requirement.
  4. // Disable it because of the substrate lib design
  5. // Example: pub PaidMembershipTermsById get(paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {}
  6. #![allow(clippy::redundant_closure_call)]
  7. pub mod genesis;
  8. pub(crate) mod mock;
  9. pub mod staking_handler;
  10. mod tests;
  11. use codec::{Codec, Decode, Encode};
  12. use frame_support::traits::Currency;
  13. use frame_support::{decl_event, decl_module, decl_storage, ensure, Parameter};
  14. use frame_system::{ensure_root, ensure_signed};
  15. use sp_arithmetic::traits::{BaseArithmetic, One};
  16. use sp_runtime::traits::{MaybeSerialize, Member};
  17. use sp_std::borrow::ToOwned;
  18. use sp_std::vec;
  19. use sp_std::vec::Vec;
  20. use common::currency::{BalanceOf, GovernanceCurrency};
  21. //TODO: Convert errors to the Substrate decl_error! macro.
  22. /// Result with string error message. This exists for backward compatibility purpose.
  23. pub type DispatchResult = Result<(), &'static str>;
  24. pub trait Trait: frame_system::Trait + GovernanceCurrency + pallet_timestamp::Trait {
  25. type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
  26. type MemberId: Parameter
  27. + Member
  28. + BaseArithmetic
  29. + Codec
  30. + Default
  31. + Copy
  32. + MaybeSerialize
  33. + PartialEq;
  34. type PaidTermId: Parameter
  35. + Member
  36. + BaseArithmetic
  37. + Codec
  38. + Default
  39. + Copy
  40. + MaybeSerialize
  41. + PartialEq;
  42. type SubscriptionId: Parameter
  43. + Member
  44. + BaseArithmetic
  45. + Codec
  46. + Default
  47. + Copy
  48. + MaybeSerialize
  49. + PartialEq;
  50. /// Describes the common type for the working group members (workers).
  51. type ActorId: Parameter
  52. + Member
  53. + BaseArithmetic
  54. + Codec
  55. + Default
  56. + Copy
  57. + MaybeSerialize
  58. + PartialEq
  59. + Ord;
  60. }
  61. const FIRST_PAID_TERMS_ID: u8 = 1;
  62. // Default paid membership terms
  63. pub const DEFAULT_PAID_TERM_ID: u8 = 0;
  64. // Default user info constraints
  65. const DEFAULT_MIN_HANDLE_LENGTH: u32 = 5;
  66. const DEFAULT_MAX_HANDLE_LENGTH: u32 = 40;
  67. const DEFAULT_MAX_AVATAR_URI_LENGTH: u32 = 1024;
  68. const DEFAULT_MAX_ABOUT_TEXT_LENGTH: u32 = 2048;
  69. /// Public membership object alias.
  70. pub type Membership<T> = MembershipObject<
  71. <T as frame_system::Trait>::BlockNumber,
  72. <T as pallet_timestamp::Trait>::Moment,
  73. <T as Trait>::PaidTermId,
  74. <T as Trait>::SubscriptionId,
  75. <T as frame_system::Trait>::AccountId,
  76. >;
  77. #[derive(Encode, Decode, Default)]
  78. /// Stored information about a registered user
  79. pub struct MembershipObject<BlockNumber, Moment, PaidTermId, SubscriptionId, AccountId> {
  80. /// The unique handle chosen by member
  81. pub handle: Vec<u8>,
  82. /// A Url to member's Avatar image
  83. pub avatar_uri: Vec<u8>,
  84. /// Short text chosen by member to share information about themselves
  85. pub about: Vec<u8>,
  86. /// Block number when member was registered
  87. pub registered_at_block: BlockNumber,
  88. /// Timestamp when member was registered
  89. pub registered_at_time: Moment,
  90. /// How the member was registered
  91. pub entry: EntryMethod<PaidTermId, AccountId>,
  92. /// Whether the member is suspended or not.
  93. pub suspended: bool,
  94. /// The type of subscription the member has purchased if any.
  95. pub subscription: Option<SubscriptionId>,
  96. /// Member's root account id. Only the root account is permitted to set a new root account
  97. /// and update the controller account. Other modules may only allow certain actions if
  98. /// signed with root account. It is intended to be an account that can remain offline and
  99. /// potentially hold a member's funds, and be a source for staking roles.
  100. pub root_account: AccountId,
  101. /// Member's controller account id. This account is intended to be used by
  102. /// a member to act under their identity in other modules. It will usually be used more
  103. /// online and will have less funds in its balance.
  104. pub controller_account: AccountId,
  105. }
  106. // Contains valid or default user details
  107. struct ValidatedUserInfo {
  108. handle: Vec<u8>,
  109. avatar_uri: Vec<u8>,
  110. about: Vec<u8>,
  111. }
  112. #[derive(Encode, Decode, Debug, PartialEq)]
  113. pub enum EntryMethod<PaidTermId, AccountId> {
  114. Paid(PaidTermId),
  115. Screening(AccountId),
  116. Genesis,
  117. }
  118. /// Must be default constructible because it indirectly is a value in a storage map.
  119. /// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
  120. impl<PaidTermId, AccountId> Default for EntryMethod<PaidTermId, AccountId> {
  121. fn default() -> Self {
  122. Self::Genesis
  123. }
  124. }
  125. #[derive(Encode, Decode, Eq, PartialEq, Default)]
  126. pub struct PaidMembershipTerms<Balance> {
  127. /// Quantity of native tokens which must be provably burned
  128. pub fee: Balance,
  129. /// String of capped length describing human readable conditions which are being agreed upon
  130. pub text: Vec<u8>,
  131. }
  132. decl_storage! {
  133. trait Store for Module<T: Trait> as Membership {
  134. /// MemberId to assign to next member that is added to the registry, and is also the
  135. /// total number of members created. MemberIds start at Zero.
  136. pub NextMemberId get(fn members_created) : T::MemberId;
  137. /// Mapping of member's id to their membership profile
  138. pub MembershipById get(fn membership) : map hasher(blake2_128_concat)
  139. T::MemberId => Membership<T>;
  140. /// Mapping of a root account id to vector of member ids it controls.
  141. pub(crate) MemberIdsByRootAccountId : map hasher(blake2_128_concat)
  142. T::AccountId => Vec<T::MemberId>;
  143. /// Mapping of a controller account id to vector of member ids it controls
  144. pub(crate) MemberIdsByControllerAccountId : map hasher(blake2_128_concat)
  145. T::AccountId => Vec<T::MemberId>;
  146. /// Registered unique handles and their mapping to their owner
  147. pub MemberIdByHandle get(fn handles) : map hasher(blake2_128_concat)
  148. Vec<u8> => T::MemberId;
  149. /// Next paid membership terms id
  150. pub NextPaidMembershipTermsId get(fn next_paid_membership_terms_id) :
  151. T::PaidTermId = T::PaidTermId::from(FIRST_PAID_TERMS_ID);
  152. /// Paid membership terms record
  153. // Remember to add _genesis_phantom_data: std::marker::PhantomData{} to membership
  154. // genesis config if not providing config() or extra_genesis
  155. pub PaidMembershipTermsById get(fn paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {
  156. // This method only gets called when initializing storage, and is
  157. // compiled as native code. (Will be called when building `raw` chainspec)
  158. // So it can't be relied upon to initialize storage for runtimes updates.
  159. // Initialization for updated runtime is done in run_migration()
  160. let terms = PaidMembershipTerms {
  161. fee: config.default_paid_membership_fee,
  162. text: Vec::default(),
  163. };
  164. vec![(T::PaidTermId::from(DEFAULT_PAID_TERM_ID), terms)]
  165. }) : map hasher(blake2_128_concat) T::PaidTermId => PaidMembershipTerms<BalanceOf<T>>;
  166. /// Active Paid membership terms
  167. pub ActivePaidMembershipTerms get(fn active_paid_membership_terms) :
  168. Vec<T::PaidTermId> = vec![T::PaidTermId::from(DEFAULT_PAID_TERM_ID)];
  169. /// Is the platform is accepting new members or not
  170. pub NewMembershipsAllowed get(fn new_memberships_allowed) : bool = true;
  171. pub ScreeningAuthority get(fn screening_authority) : T::AccountId;
  172. // User Input Validation parameters - do these really need to be state variables
  173. // I don't see a need to adjust these in future?
  174. pub MinHandleLength get(fn min_handle_length) : u32 = DEFAULT_MIN_HANDLE_LENGTH;
  175. pub MaxHandleLength get(fn max_handle_length) : u32 = DEFAULT_MAX_HANDLE_LENGTH;
  176. pub MaxAvatarUriLength get(fn max_avatar_uri_length) : u32 = DEFAULT_MAX_AVATAR_URI_LENGTH;
  177. pub MaxAboutTextLength get(fn max_about_text_length) : u32 = DEFAULT_MAX_ABOUT_TEXT_LENGTH;
  178. }
  179. add_extra_genesis {
  180. config(default_paid_membership_fee): BalanceOf<T>;
  181. config(members) : Vec<genesis::Member<T::MemberId, T::AccountId, T::Moment>>;
  182. build(|config: &GenesisConfig<T>| {
  183. for member in &config.members {
  184. let checked_user_info = <Module<T>>::check_user_registration_info(
  185. Some(member.handle.clone().into_bytes()),
  186. Some(member.avatar_uri.clone().into_bytes()),
  187. Some(member.about.clone().into_bytes())
  188. ).expect("Importing Member Failed");
  189. let member_id = <Module<T>>::insert_member(
  190. &member.root_account,
  191. &member.controller_account,
  192. &checked_user_info,
  193. EntryMethod::Genesis,
  194. T::BlockNumber::from(1),
  195. member.registered_at_time
  196. ).expect("Importing Member Failed");
  197. // ensure imported member id matches assigned id
  198. assert_eq!(member_id, member.member_id, "Import Member Failed: MemberId Incorrect");
  199. }
  200. });
  201. }
  202. }
  203. decl_event! {
  204. pub enum Event<T> where
  205. <T as frame_system::Trait>::AccountId,
  206. <T as Trait>::MemberId,
  207. {
  208. MemberRegistered(MemberId, AccountId),
  209. MemberUpdatedAboutText(MemberId),
  210. MemberUpdatedAvatar(MemberId),
  211. MemberUpdatedHandle(MemberId),
  212. MemberSetRootAccount(MemberId, AccountId),
  213. MemberSetControllerAccount(MemberId, AccountId),
  214. }
  215. }
  216. decl_module! {
  217. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  218. fn deposit_event() = default;
  219. /// Non-members can buy membership
  220. #[weight = 10_000_000] // TODO: adjust weight
  221. pub fn buy_membership(
  222. origin,
  223. paid_terms_id: T::PaidTermId,
  224. handle: Option<Vec<u8>>,
  225. avatar_uri: Option<Vec<u8>>,
  226. about: Option<Vec<u8>>
  227. ) {
  228. let who = ensure_signed(origin)?;
  229. // make sure we are accepting new memberships
  230. ensure!(Self::new_memberships_allowed(), "new members not allowed");
  231. // ensure paid_terms_id is active
  232. let terms = Self::ensure_active_terms_id(paid_terms_id)?;
  233. // ensure enough free balance to cover terms fees
  234. ensure!(T::Currency::can_slash(&who, terms.fee), "not enough balance to buy membership");
  235. let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
  236. let member_id = Self::insert_member(
  237. &who,
  238. &who,
  239. &user_info,
  240. EntryMethod::Paid(paid_terms_id),
  241. <frame_system::Module<T>>::block_number(),
  242. <pallet_timestamp::Module<T>>::now()
  243. )?;
  244. let _ = T::Currency::slash(&who, terms.fee);
  245. Self::deposit_event(RawEvent::MemberRegistered(member_id, who));
  246. }
  247. /// Change member's about text
  248. #[weight = 10_000_000] // TODO: adjust weight
  249. pub fn change_member_about_text(origin, member_id: T::MemberId, text: Vec<u8>) {
  250. let sender = ensure_signed(origin)?;
  251. let membership = Self::ensure_membership(member_id)?;
  252. ensure!(membership.controller_account == sender, "only controller account can update member about text");
  253. Self::_change_member_about_text(member_id, &text)?;
  254. }
  255. /// Change member's avatar
  256. #[weight = 10_000_000] // TODO: adjust weight
  257. pub fn change_member_avatar(origin, member_id: T::MemberId, uri: Vec<u8>) {
  258. let sender = ensure_signed(origin)?;
  259. let membership = Self::ensure_membership(member_id)?;
  260. ensure!(membership.controller_account == sender, "only controller account can update member avatar");
  261. Self::_change_member_avatar(member_id, &uri)?;
  262. }
  263. /// Change member's handle. Will ensure new handle is unique and old one will be available
  264. /// for other members to use.
  265. #[weight = 10_000_000] // TODO: adjust weight
  266. pub fn change_member_handle(origin, member_id: T::MemberId, handle: Vec<u8>) {
  267. let sender = ensure_signed(origin)?;
  268. let membership = Self::ensure_membership(member_id)?;
  269. ensure!(membership.controller_account == sender, "only controller account can update member handle");
  270. Self::_change_member_handle(member_id, handle)?;
  271. }
  272. /// Update member's all or some of handle, avatar and about text.
  273. #[weight = 10_000_000] // TODO: adjust weight
  274. pub fn update_membership(
  275. origin,
  276. member_id: T::MemberId,
  277. handle: Option<Vec<u8>>,
  278. avatar_uri: Option<Vec<u8>>,
  279. about: Option<Vec<u8>>
  280. ) {
  281. let sender = ensure_signed(origin)?;
  282. let membership = Self::ensure_membership(member_id)?;
  283. ensure!(membership.controller_account == sender, "only controller account can update member info");
  284. if let Some(uri) = avatar_uri {
  285. Self::_change_member_avatar(member_id, &uri)?;
  286. }
  287. if let Some(about) = about {
  288. Self::_change_member_about_text(member_id, &about)?;
  289. }
  290. if let Some(handle) = handle {
  291. Self::_change_member_handle(member_id, handle)?;
  292. }
  293. }
  294. #[weight = 10_000_000] // TODO: adjust weight
  295. pub fn set_controller_account(origin, member_id: T::MemberId, new_controller_account: T::AccountId) {
  296. let sender = ensure_signed(origin)?;
  297. let mut membership = Self::ensure_membership(member_id)?;
  298. ensure!(membership.root_account == sender, "only root account can set new controller account");
  299. // only update if new_controller_account is different than current one
  300. if membership.controller_account != new_controller_account {
  301. <MemberIdsByControllerAccountId<T>>::mutate(&membership.controller_account, |ids| {
  302. ids.retain(|id| *id != member_id);
  303. });
  304. <MemberIdsByControllerAccountId<T>>::mutate(&new_controller_account, |ids| {
  305. ids.push(member_id);
  306. });
  307. membership.controller_account = new_controller_account.clone();
  308. <MembershipById<T>>::insert(member_id, membership);
  309. Self::deposit_event(RawEvent::MemberSetControllerAccount(member_id, new_controller_account));
  310. }
  311. }
  312. #[weight = 10_000_000] // TODO: adjust weight
  313. pub fn set_root_account(origin, member_id: T::MemberId, new_root_account: T::AccountId) {
  314. let sender = ensure_signed(origin)?;
  315. let mut membership = Self::ensure_membership(member_id)?;
  316. ensure!(membership.root_account == sender, "only root account can set new root account");
  317. // only update if new root account is different than current one
  318. if membership.root_account != new_root_account {
  319. <MemberIdsByRootAccountId<T>>::mutate(&membership.root_account, |ids| {
  320. ids.retain(|id| *id != member_id);
  321. });
  322. <MemberIdsByRootAccountId<T>>::mutate(&new_root_account, |ids| {
  323. ids.push(member_id);
  324. });
  325. membership.root_account = new_root_account.clone();
  326. <MembershipById<T>>::insert(member_id, membership);
  327. Self::deposit_event(RawEvent::MemberSetRootAccount(member_id, new_root_account));
  328. }
  329. }
  330. #[weight = 10_000_000] // TODO: adjust weight
  331. pub fn add_screened_member(
  332. origin,
  333. new_member_account: T::AccountId,
  334. handle: Option<Vec<u8>>,
  335. avatar_uri: Option<Vec<u8>>,
  336. about: Option<Vec<u8>>
  337. ) {
  338. // ensure sender is screening authority
  339. let sender = ensure_signed(origin)?;
  340. if <ScreeningAuthority<T>>::exists() {
  341. ensure!(sender == Self::screening_authority(), "not screener");
  342. } else {
  343. // no screening authority defined. Cannot accept this request
  344. return Err("no screening authority defined".into());
  345. }
  346. // make sure we are accepting new memberships
  347. ensure!(Self::new_memberships_allowed(), "new members not allowed");
  348. let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
  349. let member_id = Self::insert_member(
  350. &new_member_account,
  351. &new_member_account,
  352. &user_info,
  353. EntryMethod::Screening(sender),
  354. <frame_system::Module<T>>::block_number(),
  355. <pallet_timestamp::Module<T>>::now()
  356. )?;
  357. Self::deposit_event(RawEvent::MemberRegistered(member_id, new_member_account));
  358. }
  359. #[weight = 10_000_000] // TODO: adjust weight
  360. pub fn set_screening_authority(origin, authority: T::AccountId) {
  361. ensure_root(origin)?;
  362. <ScreeningAuthority<T>>::put(authority);
  363. }
  364. }
  365. }
  366. /// Reason why a given member id does not have a given account as the controller account.
  367. pub enum ControllerAccountForMemberCheckFailed {
  368. NotMember,
  369. NotControllerAccount,
  370. }
  371. pub enum MemberControllerAccountDidNotSign {
  372. UnsignedOrigin,
  373. MemberIdInvalid,
  374. SignerControllerAccountMismatch,
  375. }
  376. pub enum MemberControllerAccountMismatch {
  377. MemberIdInvalid,
  378. SignerControllerAccountMismatch,
  379. }
  380. pub enum MemberRootAccountMismatch {
  381. MemberIdInvalid,
  382. SignerRootAccountMismatch,
  383. }
  384. impl<T: Trait> Module<T> {
  385. /// Provided that the member_id exists return its membership. Returns error otherwise.
  386. pub fn ensure_membership(id: T::MemberId) -> Result<Membership<T>, &'static str> {
  387. if <MembershipById<T>>::contains_key(&id) {
  388. Ok(Self::membership(&id))
  389. } else {
  390. Err("member profile not found")
  391. }
  392. }
  393. /// Ensure that given member has given account as the controller account
  394. pub fn ensure_is_controller_account_for_member(
  395. member_id: &T::MemberId,
  396. account: &T::AccountId,
  397. ) -> Result<Membership<T>, ControllerAccountForMemberCheckFailed> {
  398. if MembershipById::<T>::contains_key(member_id) {
  399. let membership = MembershipById::<T>::get(member_id);
  400. if membership.controller_account == *account {
  401. Ok(membership)
  402. } else {
  403. Err(ControllerAccountForMemberCheckFailed::NotControllerAccount)
  404. }
  405. } else {
  406. Err(ControllerAccountForMemberCheckFailed::NotMember)
  407. }
  408. }
  409. /// Returns true if account is either a member's root or controller account
  410. pub fn is_member_account(who: &T::AccountId) -> bool {
  411. <MemberIdsByRootAccountId<T>>::contains_key(who)
  412. || <MemberIdsByControllerAccountId<T>>::contains_key(who)
  413. }
  414. fn ensure_active_terms_id(
  415. terms_id: T::PaidTermId,
  416. ) -> Result<PaidMembershipTerms<BalanceOf<T>>, &'static str> {
  417. let active_terms = Self::active_paid_membership_terms();
  418. ensure!(
  419. active_terms.iter().any(|&id| id == terms_id),
  420. "paid terms id not active"
  421. );
  422. if <PaidMembershipTermsById<T>>::contains_key(terms_id) {
  423. Ok(Self::paid_membership_terms_by_id(terms_id))
  424. } else {
  425. Err("paid membership term id does not exist")
  426. }
  427. }
  428. #[allow(clippy::ptr_arg)] // cannot change to the "&[u8]" suggested by clippy
  429. fn ensure_unique_handle(handle: &Vec<u8>) -> DispatchResult {
  430. ensure!(
  431. !<MemberIdByHandle<T>>::contains_key(handle),
  432. "handle already registered"
  433. );
  434. Ok(())
  435. }
  436. fn validate_handle(handle: &[u8]) -> DispatchResult {
  437. ensure!(
  438. handle.len() >= Self::min_handle_length() as usize,
  439. "handle too short"
  440. );
  441. ensure!(
  442. handle.len() <= Self::max_handle_length() as usize,
  443. "handle too long"
  444. );
  445. Ok(())
  446. }
  447. fn validate_text(text: &[u8]) -> Vec<u8> {
  448. let mut text = text.to_owned();
  449. text.truncate(Self::max_about_text_length() as usize);
  450. text
  451. }
  452. fn validate_avatar(uri: &[u8]) -> DispatchResult {
  453. ensure!(
  454. uri.len() <= Self::max_avatar_uri_length() as usize,
  455. "avatar uri too long"
  456. );
  457. Ok(())
  458. }
  459. /// Basic user input validation
  460. fn check_user_registration_info(
  461. handle: Option<Vec<u8>>,
  462. avatar_uri: Option<Vec<u8>>,
  463. about: Option<Vec<u8>>,
  464. ) -> Result<ValidatedUserInfo, &'static str> {
  465. // Handle is required during registration
  466. let handle = handle.ok_or("handle must be provided during registration")?;
  467. Self::validate_handle(&handle)?;
  468. let about = Self::validate_text(&about.unwrap_or_default());
  469. let avatar_uri = avatar_uri.unwrap_or_default();
  470. Self::validate_avatar(&avatar_uri)?;
  471. Ok(ValidatedUserInfo {
  472. handle,
  473. avatar_uri,
  474. about,
  475. })
  476. }
  477. fn insert_member(
  478. root_account: &T::AccountId,
  479. controller_account: &T::AccountId,
  480. user_info: &ValidatedUserInfo,
  481. entry_method: EntryMethod<T::PaidTermId, T::AccountId>,
  482. registered_at_block: T::BlockNumber,
  483. registered_at_time: T::Moment,
  484. ) -> Result<T::MemberId, &'static str> {
  485. Self::ensure_unique_handle(&user_info.handle)?;
  486. let new_member_id = Self::members_created();
  487. let membership: Membership<T> = MembershipObject {
  488. handle: user_info.handle.clone(),
  489. avatar_uri: user_info.avatar_uri.clone(),
  490. about: user_info.about.clone(),
  491. registered_at_block,
  492. registered_at_time,
  493. entry: entry_method,
  494. suspended: false,
  495. subscription: None,
  496. root_account: root_account.clone(),
  497. controller_account: controller_account.clone(),
  498. };
  499. <MemberIdsByRootAccountId<T>>::mutate(root_account, |ids| {
  500. ids.push(new_member_id);
  501. });
  502. <MemberIdsByControllerAccountId<T>>::mutate(controller_account, |ids| {
  503. ids.push(new_member_id);
  504. });
  505. <MembershipById<T>>::insert(new_member_id, membership);
  506. <MemberIdByHandle<T>>::insert(user_info.handle.clone(), new_member_id);
  507. <NextMemberId<T>>::put(new_member_id + One::one());
  508. Ok(new_member_id)
  509. }
  510. fn _change_member_about_text(id: T::MemberId, text: &[u8]) -> DispatchResult {
  511. let mut membership = Self::ensure_membership(id)?;
  512. let text = Self::validate_text(text);
  513. membership.about = text;
  514. Self::deposit_event(RawEvent::MemberUpdatedAboutText(id));
  515. <MembershipById<T>>::insert(id, membership);
  516. Ok(())
  517. }
  518. fn _change_member_avatar(id: T::MemberId, uri: &[u8]) -> DispatchResult {
  519. let mut membership = Self::ensure_membership(id)?;
  520. Self::validate_avatar(uri)?;
  521. membership.avatar_uri = uri.to_owned();
  522. Self::deposit_event(RawEvent::MemberUpdatedAvatar(id));
  523. <MembershipById<T>>::insert(id, membership);
  524. Ok(())
  525. }
  526. fn _change_member_handle(id: T::MemberId, handle: Vec<u8>) -> DispatchResult {
  527. let mut membership = Self::ensure_membership(id)?;
  528. Self::validate_handle(&handle)?;
  529. Self::ensure_unique_handle(&handle)?;
  530. <MemberIdByHandle<T>>::remove(&membership.handle);
  531. <MemberIdByHandle<T>>::insert(handle.clone(), id);
  532. membership.handle = handle;
  533. Self::deposit_event(RawEvent::MemberUpdatedHandle(id));
  534. <MembershipById<T>>::insert(id, membership);
  535. Ok(())
  536. }
  537. pub fn ensure_member_controller_account_signed(
  538. origin: T::Origin,
  539. member_id: &T::MemberId,
  540. ) -> Result<T::AccountId, MemberControllerAccountDidNotSign> {
  541. // Ensure transaction is signed.
  542. let signer_account =
  543. ensure_signed(origin).map_err(|_| MemberControllerAccountDidNotSign::UnsignedOrigin)?;
  544. // Ensure member exists
  545. let membership = Self::ensure_membership(*member_id)
  546. .map_err(|_| MemberControllerAccountDidNotSign::MemberIdInvalid)?;
  547. ensure!(
  548. membership.controller_account == signer_account,
  549. MemberControllerAccountDidNotSign::SignerControllerAccountMismatch
  550. );
  551. Ok(signer_account)
  552. }
  553. pub fn ensure_member_controller_account(
  554. signer_account: &T::AccountId,
  555. member_id: &T::MemberId,
  556. ) -> Result<(), MemberControllerAccountMismatch> {
  557. // Ensure member exists
  558. let membership = Self::ensure_membership(*member_id)
  559. .map_err(|_| MemberControllerAccountMismatch::MemberIdInvalid)?;
  560. ensure!(
  561. membership.controller_account == *signer_account,
  562. MemberControllerAccountMismatch::SignerControllerAccountMismatch
  563. );
  564. Ok(())
  565. }
  566. pub fn ensure_member_root_account(
  567. signer_account: &T::AccountId,
  568. member_id: &T::MemberId,
  569. ) -> Result<(), MemberRootAccountMismatch> {
  570. // Ensure member exists
  571. let membership = Self::ensure_membership(*member_id)
  572. .map_err(|_| MemberRootAccountMismatch::MemberIdInvalid)?;
  573. ensure!(
  574. membership.root_account == *signer_account,
  575. MemberRootAccountMismatch::SignerRootAccountMismatch
  576. );
  577. Ok(())
  578. }
  579. }