Open joepetrowski opened 1 year ago
It make sense to me. We just need to understand better the possible use cases to have a good general model for it. We probably can talk about it more offline, and write down the results here.
After looking into the on-chain functionality provided for Alliance (substrate alliance pallet) and Ambassador Program (drafted pallet) I can generalize and describe it in the next way,
The first three blocks can be implemented by a combination of
Current mismatches:
Possible solution for mismatches:
(4) block of the functionality is about managing some information/documents on-chain. It's hard to predict all possible document types, and probably impossible. But one general way to present any data, is a set of bytes. We have preimage pallet to upload any binary data. To categorize (rule, events,..) them and manage we can introduce a new preimage_registry pallet.
API:
Cons:
I believe in most of the cases we can store only the IPFS hash of the document on-chain (IPFS hash uploaded as a preimage). Check the draft implementation for the new pallet in the comment below
Every collective has a custom pallet implementation, located next to the runtime (not part of substrate), without an aim for generalization.
A concept of a pallet from the "Proposal 2", from the comment above - https://github.com/paritytech/polkadot-sdk/issues/90 The goal is to demonstrate the concept and API.
// pseudo code
mod runtime {
enum DocType {
Personal,
Events,
Rule,
}
impl pallet::DocConfig<RuntimeOrigin, AccountId> for DocType {
fn config(&self) -> pallet::DocConfig<RuntimeOrigin, AccountId> {
match &self {
DocType::Personal =>
// max 10 docs, add: any signed origin, remove: any signed origin && account who added the document
pallet::DocConfig::create(10, EnsureSigned, EnsureSigned, pallet::EnsureContributor),
DocType::Events =>
// max 20 docs, add: any member of Alliance,
// remove: AllianceFellows origin - plurality voice given by a referendum.
pallet::DocConfig::create(20, EnsureAllianceMember, EnsureAllianceFellows, ()),
DocType::Rule =>
// max 1 docs, add: AllianceFellows origin, remove: AllianceFellows origin
pallet::DocConfig::create(1, EnsureAllianceFellows, EnsureAllianceFellows, ()),
}
}
}
impl pallet::Config for Runtime {
type MaxDocuments = MaxDocuments,
type DocType = DocType,
}
}
mod pallet {
pub struct DocConfig<OuterOrigin, AccountId> {
pub max_number: u32,
pub ensure_add_origin: EnsureOrigin<OuterOrigin>,
pub ensure_remove_origin: EnsureOrigin<OuterOrigin>,
// EnsureAccount::ensure_account(origin, doc_contributor), in case a client
// wanna let only a document submitter to remove the documents of a particular type.
pub ensure_remove_account: EnsureAccount<AccountId>,
}
pub trait DocConfig<OuterOrigin, AccountId> {
fn config(&self) -> DocConfig<OuterOrigin, AccountId>;
}
pub trait Config<I: 'static = ()>: frame_system::Config {
type MaxDocuments: Get<u32>;
type DocType: DocConfig + Clone + Codec + Eq + Debug + TypeInfo + MaxEncodedLen;
}
pub type Documents<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Blake2_128Concat,
T::DocType,
BoundedVec<(PreimageHash, T::AccountId), T::MaxItems>,
ValueQuery,
>;
// will be used to removed expired documents
pub type Expire<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Blake2_128Concat,
T::BlockNumber,
BoundedVec<(PreimageHash, T::DocType>), T::MaxDocuments>,
ValueQuery,
>;
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn add_doc(origin: OriginFor<T>, doc_type: T::DocType, hash: PreimageHash, maybe_expire: Option<T::BlockNumber>) {
let config = doc_type::config();
config.ensure_add_origin::ensure_origin(origin)?;
ensure!(Self::documents_len(doc_type) > config.max_number);
ensure!(T::Preimages::len(&hash).is_some(), Error::<T, I>::PreimageNotExist);
// insert the doc
}
pub fn remove_doc(origin: OriginFor<T>, doc_type: T::DocType, hash: PreimageHash) {
let config = doc_type::config();
config.ensure_remove_origin::ensure_origin(origin)?;
let (_, contributor) = Self::document(doc_type, hash).map_err(|_| Error::<T, I>::NotFound)?;
config.ensure_remove_account::ensure_account(origin, contributor)?;
// remove the doc
}
fn documents_len(doc_type: T::DocType) -> u32 {
Documents::<T, I>::decode_len(doc_type).unwrap_or(0) as u32
}
fn document(doc_type: T::DocType, hash: PreimageHash) -> Result<(PreimageHash, T::AccountId)> {
// get a doc by type and hash
}
}
}
I am moving forward with Proposal 1 and 3. Proposal 2, does not look good enough for now.
There are ideas/initiatives for new entities that are meant to provide info on-chain that serves the network in varying capacities. These usually have no hard governing power, although they could, for example the ability to spend a limited amount from the Treasury for their purposes. But that can be handled by mapping their collective origins to some variant of
SpendOrigin
.These would generally be instances of the Collective or Ranked Collective pallet to manage membership (apply, nominate, promote, remove), create custom origins, and rank members (e.g. to only give certain tiers voting rights). These all have similar needs that can be abstracted.
BlockNumber
and are clearedon_initialize
)UnscrupulousList<AccountId, BoundedVec<u8>>
)Instead of having an Alliance pallet, Ambassador Program pallet, etc., these should just instantiate Ranked Collective for their origin types and ranking, and then use this extension to manage the on-chain info relevant to that collective's audience.